Compare commits

...

58 commits

Author SHA1 Message Date
136a0e71b7 fix: better multi check 2025-04-21 19:35:02 +02:00
fc56827706 bump: version 1.2.0a17 → 1.2.0a18 2025-04-09 21:19:54 +02:00
77490ca176 fix: version 2025-04-09 21:19:45 +02:00
65ffaef2fe bump: version 1.2.0a16 → 1.2.0a17 2025-04-09 09:02:38 +02:00
c494a4662f fix: better detection of multi variable in default attribute 2025-04-09 09:02:32 +02:00
0bc1e24d38 fix: better error message 2025-04-09 09:01:48 +02:00
e59b9e84df bump: version 1.2.0a15 → 1.2.0a16 2025-04-03 20:47:28 +02:00
62097b552a feat: reoganise param conversion + better variable validation 2025-04-03 20:47:12 +02:00
19249875c3 fix: correction in namespace calculation 2025-04-02 21:46:47 +02:00
ae89e30e60 bump: version 1.2.0a14 → 1.2.0a15 2025-04-01 22:21:37 +02:00
e1b2d36aae fix: update tests 2025-04-01 22:06:18 +02:00
cc086f086a fix: do not raise variable in property with force_optional 2025-04-01 21:10:22 +02:00
7ee65b6cab fix: update translation 2025-04-01 21:09:49 +02:00
1858eeeb06 feat: can link regexp variable 2025-04-01 08:55:00 +02:00
dc311466a2 feat: can link choice variable 2025-04-01 08:36:23 +02:00
18df35acfe bump: version 1.2.0a13 → 1.2.0a14 2025-03-30 18:42:35 +02:00
0e522db15f fix: strutural step should not be available in commandline 2025-03-30 18:42:13 +02:00
07a22d7966 bump: version 1.2.0a12 → 1.2.0a13 2025-03-30 18:38:33 +02:00
7f9d6f17d7 fix: allow no user_datas installation (for example to generate doc) 2025-03-27 20:05:20 +01:00
2654141002 bump: version 1.2.0a11 → 1.2.0a12 2025-03-19 11:43:13 +01:00
999b53889c feat: add rougail secret_manager 2025-03-19 11:43:04 +01:00
d35fe16cd9 bump: version 1.2.0a10 → 1.2.0a11 2025-02-17 15:45:33 +01:00
04aa9444a3 fix: add get remove properties 2025-02-17 15:45:27 +01:00
9c7fb1d505 bump: version 1.2.0a9 → 1.2.0a10 2025-02-17 09:21:02 +01:00
70003fcbc5 fix: we can define structural plugin when generate documentation 2025-02-17 09:20:38 +01:00
85f3b9f80f fix: if a variable in user_data not existe, it's no a warnings 2025-02-17 09:19:31 +01:00
b0687fdcc6 bump: version 1.2.0a8 → 1.2.0a9 2025-02-10 10:32:57 +01:00
cc5aaf6393 feat: can change defaut params for an option 2025-02-10 10:32:48 +01:00
df2fcb467f fix: if no description, generate negative_description too 2025-02-10 10:09:34 +01:00
7b9d7ce419 fix: error messages 2025-02-07 08:08:55 +01:00
63f76dc68f bump: version 1.2.0a7 → 1.2.0a8 2025-01-04 12:01:17 +01:00
e7e9687b8d fix: better support of not_for_commandline feature 2025-01-04 12:01:08 +01:00
0fea822c91 bump: version 1.2.0a6 → 1.2.0a7 2025-01-02 22:01:56 +01:00
ea6baa3dc2 fix: add structural_directory 2025-01-02 22:01:38 +01:00
7d830d5e40 bump: version 1.2.0a5 → 1.2.0a6 2025-01-02 21:19:40 +01:00
81d86e7b6f fix: add path.py 2025-01-02 21:19:16 +01:00
7a5a1b42e8 bump: version 1.2.0a4 → 1.2.0a5 2025-01-02 21:06:47 +01:00
3849c42cba feat: upgrade is not in formatter 2025-01-02 21:06:13 +01:00
6c1df5578f feat: remove prefix 2025-01-02 21:02:44 +01:00
dfd31c4fb4 bump: version 1.2.0a3 → 1.2.0a4 2024-12-11 21:00:22 +01:00
45959ac18b fix: remove link 2024-12-11 20:58:08 +01:00
9e8c6f96c5 feat: move test to a new project rougail-tests 2024-12-11 20:54:54 +01:00
00e0941f6e fix: reorganise user_datas 2024-12-11 20:54:03 +01:00
8afb787c98 fix: only change prefix if path is relative 2024-12-11 20:51:08 +01:00
a91a4d6a55 feat: output could have annotator 2024-12-11 20:50:15 +01:00
688aa8d053 bump: version 1.2.0a2 → 1.2.0a3 2024-11-28 08:31:00 +01:00
4c613f6425 fix: add user_datas file 2024-11-28 08:30:47 +01:00
145b7be0a5 bump: version 1.2.0a1 → 1.2.0a2 2024-11-27 16:09:18 +01:00
829d7692ad fix: separate UserDatas 2024-11-27 16:09:04 +01:00
ea34751e08 fix: options could be a list 2024-11-27 10:22:00 +01:00
c25a66f0cb feat: add "exists" attribut for a family 2024-11-27 10:14:42 +01:00
fdfb7ec73b bump: version 1.2.0a0 → 1.2.0a1 2024-11-25 09:18:04 +01:00
db768f94af fix: dynamic variable could be optional 2024-11-25 09:17:27 +01:00
9782bcde95 fix: dynamic variable declare in verion 1.0 has {{ suffix }} 2024-11-25 09:16:48 +01:00
fbb1446fa3 fix: user_data plugins could have annotator function 2024-11-25 09:14:58 +01:00
56659f7ea4 fix: do not modify a dynamic variable if has default value 2024-11-25 09:10:44 +01:00
af3f77c920 bump: version 1.1.1 → 1.2.0a0 2024-11-08 08:13:27 +01:00
e29c0c8abc feat: add force_optional option to allow charging structure even if all variables are not available 2024-11-08 08:12:33 +01:00
1218 changed files with 8110 additions and 10254 deletions
CHANGELOG.md
locale
fr/LC_MESSAGES
rougail.pot
pyproject.toml
src/rougail
tests
default_option_params/structure
dictionaries
00_0empty/tiramisu
00_0version_underscore
dictionaries/rougail
tiramisu
00_1empty_variable
dictionaries/rougail
tiramisu
00_2default_calculated
dictionaries/rougail
tiramisu
00_2default_calculated_multi
dictionaries/rougail
tiramisu
00_2default_calculated_variable_transitive
dictionaries/rougail
tiramisu
00_4load_subfolder
dictionaries
rougail
rougail2
tiramisu
00_5load_notype
dictionaries/rougail
tiramisu
00_6boolean
dictionaries/rougail
tiramisu
00_6boolean_no_mandatory
dictionaries/rougail
tiramisu
00_6choice
dictionaries/rougail
tiramisu
00_6choice_calculation
dictionaries/rougail
tiramisu
00_6choice_link
00_6choice_variable
dictionaries/rougail
tiramisu

View file

@ -1,3 +1,151 @@
## 1.2.0a18 (2025-04-09)
### Fix
- version
## 1.2.0a17 (2025-04-09)
### Fix
- better detection of multi variable in default attribute
- better error message
## 1.2.0a16 (2025-04-03)
### Feat
- reoganise param conversion + better variable validation
### Fix
- correction in namespace calculation
## 1.2.0a15 (2025-04-01)
### Feat
- can link regexp variable
- can link choice variable
### Fix
- update tests
- do not raise variable in property with force_optional
- update translation
## 1.2.0a14 (2025-03-30)
### Fix
- strutural step should not be available in commandline
## 1.2.0a13 (2025-03-27)
### Fix
- allow no user_datas installation (for example to generate doc)
## 1.2.0a12 (2025-03-19)
### Feat
- add rougail secret_manager
## 1.2.0a11 (2025-02-17)
### Fix
- add get remove properties
## 1.2.0a10 (2025-02-17)
### Fix
- we can define structural plugin when generate documentation
- if a variable in user_data not existe, it's no a warnings
## 1.2.0a9 (2025-02-10)
### Feat
- can change defaut params for an option
### Fix
- if no description, generate negative_description too
- error messages
## 1.2.0a8 (2025-01-04)
### Fix
- better support of not_for_commandline feature
## 1.2.0a7 (2025-01-02)
### Fix
- add structural_directory
## 1.2.0a6 (2025-01-02)
### Fix
- add path.py
## 1.2.0a5 (2025-01-02)
### Feat
- upgrade is not in formatter
- remove prefix
## 1.2.0a4 (2024-12-11)
### Feat
- move test to a new project rougail-tests
- output could have annotator
### Fix
- remove link
- reorganise user_datas
- only change prefix if path is relative
## 1.2.0a3 (2024-11-28)
### Fix
- add user_datas file
## 1.2.0a2 (2024-11-27)
### Feat
- add "exists" attribut for a family
### Fix
- separate UserDatas
- options could be a list
## 1.2.0a1 (2024-11-25)
### Fix
- dynamic variable could be optional
- dynamic variable declare in verion 1.0 has {{ suffix }}
- user_data plugins could have annotator function
- do not modify a dynamic variable if has default value
## 1.2.0a0 (2024-11-08)
### Feat
- add force_optional option to allow charging structure even if all variables are not available
## 1.1.1 (2024-11-06)
### Fix

View file

@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2024-10-30 13:21+0100\n"
"PO-Revision-Date: 2024-10-30 13:39+0100\n"
"POT-Creation-Date: 2025-04-09 08:23+0200\n"
"PO-Revision-Date: 2025-04-09 08:26+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
@ -16,39 +16,39 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.5\n"
#: src/rougail/annotator/family.py:142
#: src/rougail/annotator/family.py:149
msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr ""
"le mode d'une variable par défaut \"{0}\" n'est pas un mode valide, les "
"modes valides sont {1}"
#: src/rougail/annotator/family.py:148
#: src/rougail/annotator/family.py:155
msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr ""
"le mode d'une famille par défaut \"{0}\" n'est pas un mode valide, les modes "
"valides sont {1}"
#: src/rougail/annotator/family.py:180
#: src/rougail/annotator/family.py:187
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}"
msgstr ""
"le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, les modes valides "
"sont {2}"
#: src/rougail/annotator/family.py:184
#: src/rougail/annotator/family.py:191
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available"
msgstr ""
"le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, aucun mode ne sont "
"définis"
#: src/rougail/annotator/family.py:248
#: src/rougail/annotator/family.py:255
msgid ""
"the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the "
"higher family mode \"{2}\""
msgstr ""
"la variable \"{0}\" est obligatoire donc dans le mode \"{1}\" mais la "
"famille a un mode supérieur \"{2}\""
"la variable \"{0}\" est obligatoire, donc en mode \"{1}\", mais la famille a "
"un mode supérieur \"{2}\""
#: src/rougail/annotator/family.py:286
#: src/rougail/annotator/family.py:293
msgid ""
"the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode "
"\"{2}\""
@ -56,7 +56,7 @@ msgstr ""
"la variable suiveuse \"{0}\" a le mode \"{1}\" mais la variable leader a un "
"mode supérieur \"{2}\""
#: src/rougail/annotator/family.py:319
#: src/rougail/annotator/family.py:326
msgid ""
"the family \"{0}\" is in \"{1}\" mode but variables and families inside have "
"the higher modes \"{2}\""
@ -64,7 +64,7 @@ msgstr ""
"la famille \"{0}\" a le mode \"{1}\" mais les variables et les familles à "
"l'intérieur ont des modes supérieurs \"{2}\""
#: src/rougail/annotator/family.py:337
#: src/rougail/annotator/family.py:344
msgid ""
"the variable \"{0}\" is in \"{1}\" mode but family has the higher family "
"mode \"{2}\""
@ -72,37 +72,340 @@ msgstr ""
"la variable \"{0}\" est dans le mode \"{1}\" mais la famille a le mode "
"supérieur \"{2}\""
#: src/rougail/annotator/value.py:80
#: src/rougail/annotator/value.py:78
msgid "the follower \"{0}\" without multi attribute can only have one value"
msgstr ""
"la variable suiveuse \"{0}\" sans l'attribut multi peut avoir seulement une "
"valeur"
#: src/rougail/annotator/value.py:96
#: src/rougail/annotator/value.py:94
msgid "the variable \"{0}\" is multi but has a non list default value"
msgstr ""
"la variable \"{0}\" est multiple mais a une valeur par défaut sans être une "
"liste"
#: src/rougail/annotator/variable.py:192
#: src/rougail/annotator/value.py:118
msgid "the variable \"{0}\" is a \"choice\" variable but don't have any choice"
msgstr "la variable \"{0}\" a une variable à \"choix\" mais n'a aucun choix"
#: src/rougail/annotator/value.py:135
msgid "the variable \"{0}\" is a \"regexp\" variable but don't have any regexp"
msgstr ""
"la variable \"{0}\" a une variable \"regexp\" mais n'a pas de \"regexp\""
#: src/rougail/annotator/variable.py:85
msgid ""
"only \"unix_user\" or \"secret\" variable type can have \"secret_manager\" "
"attribute, but \"{0}\" has type \"{1}\""
msgstr ""
"seul une variable de type \"unix_user\" ou \"secret\" peut avoir l'attribut "
"\"secret_manager\", mais \"{0}\" a le type \"{1}\""
#: src/rougail/annotator/variable.py:88
msgid ""
"the variable \"{0}\" has attribute \"secret_manager\" but is a multi variable"
msgstr ""
"la variable \"{0}\" a l'attribut \"secret_manager\" mais est une variable "
"multiple"
#: src/rougail/annotator/variable.py:91
msgid ""
"the variable \"{0}\" has attribute \"secret_manager\" so must not have "
"default value"
msgstr ""
"la variable \"{0}\" a l'attribut \"secret_manager\" donc ne devrait pas "
"avoir de valeur par défaut"
#: src/rougail/annotator/variable.py:197
msgid ""
"the variable \"{0}\" has regexp attribut but has not the \"regexp\" type"
msgstr ""
"la variable \"{0}\" a un attribut regexp mais n'a pas le type \"regexp\""
#: src/rougail/annotator/variable.py:235
#: src/rougail/annotator/variable.py:240
msgid ""
"the variable \"{0}\" has choices attribut but has not the \"choice\" type"
msgstr ""
"la variable \"{0}\" a un attribut choices mais n'a pas le type \"choice\""
#: src/rougail/annotator/variable.py:263
#: src/rougail/annotator/variable.py:268
msgid ""
"the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}"
msgstr ""
"la variable \"{0}\" a la valeur par défaut invalide \"{1}\" devrait être {2}"
#: src/rougail/convert.py:268
#: src/rougail/config.py:234
msgid "File with functions"
msgstr "Fichier avec les fonctions"
#: src/rougail/config.py:246
msgid "All modes level available"
msgstr "Tous les niveaux de modes valides"
#: src/rougail/config.py:258
msgid "Default mode for a family"
msgstr "Mode par défaut pour une famille"
#: src/rougail/config.py:278
msgid "Default mode for a variable"
msgstr "Mode par défaut pour une variable"
#: src/rougail/config.py:302
msgid "Option name for the base option"
msgstr "Nom de l'option pour l'option de base"
#: src/rougail/config.py:307
msgid "In cache file, do not importation of Tiramisu and other dependencies"
msgstr ""
"Dans le fichier de cache, ne pas importer Tiramisu et autres dépendances"
#: src/rougail/config.py:308
msgid "In cache file, do importation of Tiramisu and other dependencies"
msgstr "Dans le fichier de cache, importer Tiramisu et autres dépendances"
#: src/rougail/config.py:313
msgid "Tiramisu cache filename"
msgstr "Nom du fichier du cache Tiramisu"
#: src/rougail/config.py:321
msgid "Name of internal functions that we can use as a function"
msgstr ""
"Nom des fonctions internes qu'il est possible d'utiliser comme fonction"
#: src/rougail/config.py:327
msgid "Name of extra annotators"
msgstr "Nom des annotators supplémentaires"
#: src/rougail/config.py:333
msgid "Suffix add to generated options name"
msgstr "Suffix ajouté pour généré le nom des options"
#: src/rougail/config.py:339
msgid "Every variables in calculation are optionals"
msgstr "Toutes les variables dans un calcul sont optionnelles"
#: src/rougail/config.py:340
msgid "Variables in calculation are not optional by default"
msgstr "Les variables dans un calcul sont optionnelles par défaut"
#: src/rougail/config.py:344 src/rougail/config.py:345
msgid "Loads redefine variables even if there don't already exists"
msgstr "Charger les variables redéfinis même si elles n'existe pas"
#: src/rougail/config.py:352
msgid "The secret pattern to build item name in Bitwarden"
msgstr "Le patron de secret pour construire le nom de l'élément dans Bitwarden"
#: src/rougail/config.py:353
msgid "The pattern is in Jinja format"
msgstr "Le patron est au format Jinja"
#: src/rougail/config.py:461
msgid "Default parameters for option type"
msgstr "Paramètre par défaut pour le type d'option"
#: src/rougail/convert.py:281
msgid "unknown type {0} for {1}"
msgstr "type {0} inconnu pour {1}"
#: src/rougail/convert.py:413
msgid "family \"{0}\" define multiple time"
msgstr "la famille \"{0}\" est définit plusieurs fois"
#: src/rougail/convert.py:681
msgid "variable \"{0}\" define multiple time"
msgstr "la variable \"{0}\" est définit plusieurs fois"
#: src/rougail/convert.py:787
msgid "params must be a dict for {0}"
msgstr "params doit être une dict pour {0}"
#: src/rougail/convert.py:809
msgid "\"{0}\" has an invalid \"params\" for {1}: {2}"
msgstr "\"{0}\" a un attribut \"params\" invalide pour {1}: {2}"
#: src/rougail/convert.py:823
msgid "secret_manager must be a dict for {0}"
msgstr "secret_manager doit être une dict pour {0}"
#: src/rougail/convert.py:1154
msgid "Cannot execute annotate multiple time"
msgstr "Ne peut exécuter l'annotation plusieurs fois"
#: src/rougail/convert.py:1161
msgid ""
"invalid \"structural\" definition ({0}), we cannot load any structural file!"
msgstr ""
"définition invalide pour la définition des \"structures\" ({0}), aucun "
"fichier de structure ne peut être chargé !"
#: src/rougail/error.py:67 src/rougail/user_datas.py:251
msgid "{0} in {1}"
msgstr "{0} dans {1}"
#: src/rougail/object_model.py:50
msgid "unknown boolean value \"{0}\""
msgstr "valeur du booléen inconnue \"{0}\""
#: src/rougail/object_model.py:182
msgid "cannot find variable \"{0}\" defined in attribute \"{1}\" for \"{2}\""
msgstr ""
"ne peut trouver la variable \"{0}\" défini dans l'attribut \"{1}\" pour "
"\"{2}\""
#: src/rougail/object_model.py:186
msgid ""
"the variable \"{0}\" is in fact a family in attribute \"{1}\" for \"{2}\""
msgstr ""
"la variable \"{0}\" est en faite une famille dans l'attribut \"{1}\" pour "
"\"{2}\""
#: src/rougail/object_model.py:189 src/rougail/object_model.py:473
msgid "unknown object \"{0}\" in attribute \"{1}\" for \"{2}\""
msgstr "objet inconnu \"{0}\" dans l'attribut \"{1}\" pour \"{2}\""
#: src/rougail/object_model.py:206
msgid ""
"identifier parameter for \"{0}\" in \"{1}\" cannot be set none dynamic family"
msgstr ""
"le paramètre identifier pour \"{0}\" dans \"{1}\" ne peut être placé pour "
"une famille non dynamique"
#: src/rougail/object_model.py:229
msgid "cannot find variable \"{0}\" defined in \"{1}\" for \"{2}\""
msgstr "ne peut trouver la variable \"{0}\" défini dans \"{1}\" pour \"{2}\""
#: src/rougail/object_model.py:232
msgid "variable \"{0}\" defined in \"{1}\" for \"{2}\" is a dynamic variable"
msgstr ""
"la variable \"{0}\" défini dans \"{1}\" pour \"{2}\" est une variable "
"dynamique"
#: src/rougail/object_model.py:246
msgid ""
"the variable \"{0}\" is not a follower, so cannot have index type for param "
"in \"{1}\""
msgstr ""
"la variable \"{0}\" n'est pas suiveuse, donc ne peut avoir de type index "
"comme paramètre dans \"{1}\""
#: src/rougail/object_model.py:470
msgid ""
"a variable \"{0}\" is needs in attribute \"{1}\" for \"{2}\" but it's a "
"family"
msgstr ""
"une variable \"{0}\" est nécessaire pour l'attribut \"{1}\" pour \"{2}\" "
"mais c'est une famille"
#: src/rougail/object_model.py:491
msgid "Variable not found \"{0}\" for attribut \"{1}\" in variable \"{2}\""
msgstr ""
"La variable \"{0}\" n'est pas trouvé pour l'attribut \"{1}\" dans la "
"variable \"{2}\""
#: src/rougail/object_model.py:536
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" "
"is multi but is inside a list"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable \"{2}\" est "
"multiple mais est dans une liste"
#: src/rougail/object_model.py:539
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" "
"is not multi but is not inside a list"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable \"{2}\" "
"n'est pas multiple mais n'est pas dans une liste"
#: src/rougail/object_model.py:542
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", it's a list"
msgstr "la variable \"{0}\" a un attribut invalide \"{1}\", c'est une liste"
#: src/rougail/object_model.py:550
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" "
"is a multi"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable \"{2}\" est "
"multiple"
#: src/rougail/object_model.py:572
msgid ""
"\"{0}\" attribut shall not have an \"optional\" attribute for variable "
"\"{1}\""
msgstr ""
"l'attribut \"{0}\" ne devrait pas avoir d'attribut \"optional\" pour la "
"variable \"{1}\""
#: src/rougail/object_model.py:584
msgid ""
"variable \"{0}\" has a default value calculated with \"{1}\" which has "
"incompatible type"
msgstr ""
"la variable \"{0}\" a une valeur par défaut calculé a partir de \"{1}\" "
"laquelle a un type incompatible"
#: src/rougail/object_model.py:620
msgid ""
"\"when\" is not allowed in format version 1.0 for attribute \"{0}\" for "
"variable \"{1}\""
msgstr ""
"\"when\" n'est pas autorisé avec le format en version 1.0 pour l'attribut "
"\"{0}\" pour la variable \"{1}\""
#: src/rougail/object_model.py:623
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", \"when\" and "
"\"when_not\" cannot set together"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", \"when\" et \"when_not\" "
"ne peuvent pas être défini ensemble"
#: src/rougail/object_model.py:629
msgid ""
"\"when_not\" is not allowed in format version 1.0 for attribute \"{0}\" for "
"variable \"{1}\""
msgstr ""
"\"when_not\" n'est pas autorisé au format 1.0 pour l'attribut \"{0}\" pour "
"la variable variable \"{1}\""
#: src/rougail/object_model.py:682
msgid ""
"cannot find variable \"{0}\" for the information \"{1}\" when calculating "
"\"{2}\""
msgstr ""
"ne peut trouver la variable \"{0}\" pour l'information \"{1}\" lors du "
"calcul de \"{2}\""
#: src/rougail/object_model.py:685
msgid ""
"identifier not allowed for the information \"{0}\" when calculating \"{1}\""
msgstr ""
"identifier n'est pas autorisé pour l'information \"{0}\" lors du calcul de "
"\"{1}\""
#: src/rougail/object_model.py:734
msgid "\"when\" is not allowed in format version 1.0 for attribute \"{0}\""
msgstr "\"when\" n'est pas autorisé au format 1.0 pour l'attribut \"{0}\""
#: src/rougail/object_model.py:738 src/rougail/object_model.py:746
msgid ""
"the identifier has an invalid attribute \"{0}\", \"when\" and \"when_not\" "
"cannot set together"
msgstr ""
"l'identifiant a un attribut invalide \"{0}\", \"when\" et \"when_not\" ne "
"peuvent pas être défini ensemble"
#: src/rougail/object_model.py:768
msgid ""
"the variable \"{0}\" is not a follower, so cannot have index type for \"{1}\""
msgstr ""
"la variable \"{0}\" n'est pas suiveuse, donc ne peut avoir de type index "
"pour \"{1}\""
#: src/rougail/path.py:206
msgid ""
"A variable or a family located in the \"{0}\" namespace shall not be used in "
"the \"{1}\" namespace"
@ -110,38 +413,22 @@ msgstr ""
"Une variable ou une famille localisé dans l'espace de nom \"{0}\" ne devrait "
"pas être utilisé dans l'espace de nom \"{1}\""
#: src/rougail/convert.py:462
msgid "unknown type {0} for {1}"
msgstr "type {0} inconnu pour {1}"
#: src/rougail/convert.py:1323
msgid "duplicate dictionary file name {0}"
msgstr "nom de fichier {0} de dictionnaire dupliqué"
#: src/rougail/convert.py:1370
msgid "Cannot execute annotate multiple time"
msgstr "Ne peut exécuter l'annotation plusieurs fois"
#: src/rougail/error.py:70
msgid "{0} in {1}"
msgstr "{0} dans {1}"
#: src/rougail/structural_commandline/annotator.py:70
#: src/rougail/structural_commandline/annotator.py:67
msgid "alternative_name \"{0}\" conflict with \"--help\""
msgstr "alternative_name \"{0}\" est en conflit avec \"--help\""
#: src/rougail/structural_commandline/annotator.py:73
#: src/rougail/structural_commandline/annotator.py:72
msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\""
msgstr "conflit dans les \"alternative_name\" \"{0}\": \"{1}\" et \"{2}\""
#: src/rougail/structural_commandline/annotator.py:96
#: src/rougail/structural_commandline/annotator.py:98
msgid ""
"negative_description is mandatory for boolean variable, but \"{0}\" hasn't"
msgstr ""
"l'attribut negative_description est obligatoire pour des variables "
"\"boolean\", mais \"{0}\" n'en a pas"
#: src/rougail/structural_commandline/annotator.py:105
#: src/rougail/structural_commandline/annotator.py:106
msgid ""
"negative_description is only available for boolean variable, but \"{0}\" is "
"\"{1}\""
@ -149,11 +436,70 @@ msgstr ""
"l'attribut negative_description est seulement valide pour des variables "
"\"boolean\", mais \"{0}\" est \"{1}\""
#: src/rougail/update/update.py:741
msgid "not a XML file: {0}"
msgstr "fichier XML invalid : {0}"
#: src/rougail/structural_directory/__init__.py:127
msgid "duplicate dictionary file name {0}"
msgstr "nom de fichier {0} de dictionnaire dupliqué"
#: src/rougail/utils.py:58
#: src/rougail/structural_directory/config.py:32
msgid "Directories where dictionary files are placed"
msgstr "Répertoires où sont placés les fichiers de structure"
#: src/rougail/structural_directory/config.py:46
msgid "Sort dictionaries from differents directories"
msgstr "Trier les fichiers de structure à partir de différents répertoires"
#: src/rougail/structural_directory/config.py:56
msgid "Main namespace name"
msgstr "Nom de l'espace de nom principal"
#: src/rougail/structural_directory/config.py:67
msgid "Extra namespaces"
msgstr "Espaces de nom supplémentaires"
#: src/rougail/structural_directory/config.py:76
msgid "Extra namespace name"
msgstr "Nom de l'espace de nom supplémentaire"
#: src/rougail/structural_directory/config.py:82
msgid "Directories where extra dictionary files are placed"
msgstr ""
"Répertoires où sont placés les fichiers de structure de l'espace de nom "
"supplémentaire"
#: src/rougail/tiramisu.py:167
msgid ""
"cannot calculating \"{0}\" attribute for variable \"{1}\" in {2} with "
"parameters \"{3}\": {4}"
msgstr ""
"ne peut calculer l'attribut \"{0}\" pour la variable \"{1}\" dans {2} avec "
"les paramètres \"{3}\" : {4}"
#: src/rougail/tiramisureflector.py:369
msgid "internal error, {0} is not a dynamic variable"
msgstr "erreur interne, \"{0}\" n'est pas une variable dynamique"
#: src/rougail/user_datas.py:116
msgid ""
"cannot load variable path \"{0}\", the identifier \"{1}\" is not valid in {2}"
msgstr ""
"ne peut charger la variable \"{0}\", l'identifiant \"{1}\" n'est pas valide "
"dans {2}"
#: src/rougail/user_datas.py:171
msgid "the variable \"{0}\" contains secrets and should not be defined in {1}"
msgstr ""
"la variable \"{0}\" contient des secrets et ne devrait pas être défini dans "
"{1}"
#: src/rougail/user_datas.py:234
msgid "the variable \"{0}\" is a family in {1}"
msgstr "la variable \"{0}\" est une famille dans {1}"
#: src/rougail/user_datas.py:249
msgid "{0} loaded from {1}"
msgstr "{0} chargée depuis {1}"
#: src/rougail/utils.py:55
msgid ""
"invalid variable or family name \"{0}\" must only contains lowercase ascii "
"character, number or _"
@ -161,6 +507,40 @@ msgstr ""
"nom invalide pour la variable ou famille \"{0}\" doit seulement contenir des "
"caractères ascii minuscule, nombre or _"
#: src/rougail/utils.py:120
#: src/rougail/utils.py:113
msgid "error in jinja \"{0}\" for the variable \"{1}\": {2}"
msgstr "erreur dans Jinja \"{0}\" pour la variable \"{1}\": {2}"
#~ msgid "error in {0}: {1}"
#~ msgstr "erreur dans {0} : {1}"
#~ msgid "cannot find variable \"{0}\" from {1}: {2}"
#~ msgstr "ne peut trouver la variable \"{0}\" depuis {1} : {2}"
#~ msgid "the option \"{0}\" is an option description"
#~ msgstr "l'option \"{0}\" est une option description"
#~ msgid "Update dictionaries to newest Rougail format version"
#~ msgstr ""
#~ "Mettre à jour le fichier de structure vers la dernière version du format "
#~ "de Rougail"
#~ msgid "Do not update dictionaries to newest Rougail format version"
#~ msgstr ""
#~ "Ne pas mettre à jour le fichier de structure vers la dernière version du "
#~ "format de Rougail"
#~ msgid "Update informations"
#~ msgstr "Mise à jour des informations"
#~ msgid "Directories where dictionary files will be placed"
#~ msgstr "Répertoires où sont placés les fichiers de structure"
#~ msgid "Directories where extra files will be placed"
#~ msgstr "Répertoires où sont placés les fichiers de structure supplémentaire"
#~ msgid "The family \"{0}\" already exists and it is not redefined"
#~ msgstr "La famille \"{0}\" existe déjà et n'est pas redéfinie"
#~ msgid "not a XML file: {0}"
#~ msgstr "fichier XML invalid : {0}"

View file

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-11-04 12:04+0100\n"
"POT-Creation-Date: 2025-04-09 08:26+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,78 +15,282 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: src/rougail/annotator/family.py:139
#: src/rougail/annotator/family.py:149
msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr ""
#: src/rougail/annotator/family.py:145
#: src/rougail/annotator/family.py:155
msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr ""
#: src/rougail/annotator/family.py:177
#: src/rougail/annotator/family.py:187
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}"
msgstr ""
#: src/rougail/annotator/family.py:181
#: src/rougail/annotator/family.py:191
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available"
msgstr ""
#: src/rougail/annotator/family.py:245
#: src/rougail/annotator/family.py:255
msgid "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the higher family mode \"{2}\""
msgstr ""
#: src/rougail/annotator/family.py:283
#: src/rougail/annotator/family.py:293
msgid "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode \"{2}\""
msgstr ""
#: src/rougail/annotator/family.py:316
#: src/rougail/annotator/family.py:326
msgid "the family \"{0}\" is in \"{1}\" mode but variables and families inside have the higher modes \"{2}\""
msgstr ""
#: src/rougail/annotator/family.py:334
#: src/rougail/annotator/family.py:344
msgid "the variable \"{0}\" is in \"{1}\" mode but family has the higher family mode \"{2}\""
msgstr ""
#: src/rougail/annotator/value.py:77
#: src/rougail/annotator/value.py:78
msgid "the follower \"{0}\" without multi attribute can only have one value"
msgstr ""
#: src/rougail/annotator/value.py:93
#: src/rougail/annotator/value.py:94
msgid "the variable \"{0}\" is multi but has a non list default value"
msgstr ""
#: src/rougail/annotator/variable.py:189
#: src/rougail/annotator/value.py:118
msgid "the variable \"{0}\" is a \"choice\" variable but don't have any choice"
msgstr ""
#: src/rougail/annotator/value.py:135
msgid "the variable \"{0}\" is a \"regexp\" variable but don't have any regexp"
msgstr ""
#: src/rougail/annotator/variable.py:85
msgid "only \"unix_user\" or \"secret\" variable type can have \"secret_manager\" attribute, but \"{0}\" has type \"{1}\""
msgstr ""
#: src/rougail/annotator/variable.py:88
msgid "the variable \"{0}\" has attribute \"secret_manager\" but is a multi variable"
msgstr ""
#: src/rougail/annotator/variable.py:91
msgid "the variable \"{0}\" has attribute \"secret_manager\" so must not have default value"
msgstr ""
#: src/rougail/annotator/variable.py:197
msgid "the variable \"{0}\" has regexp attribut but has not the \"regexp\" type"
msgstr ""
#: src/rougail/annotator/variable.py:232
#: src/rougail/annotator/variable.py:240
msgid "the variable \"{0}\" has choices attribut but has not the \"choice\" type"
msgstr ""
#: src/rougail/annotator/variable.py:260
#: src/rougail/annotator/variable.py:268
msgid "the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}"
msgstr ""
#: src/rougail/convert.py:281
msgid "A variable or a family located in the \"{0}\" namespace shall not be used in the \"{1}\" namespace"
#: src/rougail/config.py:234
msgid "File with functions"
msgstr ""
#: src/rougail/convert.py:475
#: src/rougail/config.py:246
msgid "All modes level available"
msgstr ""
#: src/rougail/config.py:258
msgid "Default mode for a family"
msgstr ""
#: src/rougail/config.py:278
msgid "Default mode for a variable"
msgstr ""
#: src/rougail/config.py:302
msgid "Option name for the base option"
msgstr ""
#: src/rougail/config.py:307
msgid "In cache file, do not importation of Tiramisu and other dependencies"
msgstr ""
#: src/rougail/config.py:308
msgid "In cache file, do importation of Tiramisu and other dependencies"
msgstr ""
#: src/rougail/config.py:313
msgid "Tiramisu cache filename"
msgstr ""
#: src/rougail/config.py:321
msgid "Name of internal functions that we can use as a function"
msgstr ""
#: src/rougail/config.py:327
msgid "Name of extra annotators"
msgstr ""
#: src/rougail/config.py:333
msgid "Suffix add to generated options name"
msgstr ""
#: src/rougail/config.py:339
msgid "Every variables in calculation are optionals"
msgstr ""
#: src/rougail/config.py:340
msgid "Variables in calculation are not optional by default"
msgstr ""
#: src/rougail/config.py:344 src/rougail/config.py:345
msgid "Loads redefine variables even if there don't already exists"
msgstr ""
#: src/rougail/config.py:352
msgid "The secret pattern to build item name in Bitwarden"
msgstr ""
#: src/rougail/config.py:353
msgid "The pattern is in Jinja format"
msgstr ""
#: src/rougail/config.py:461
msgid "Default parameters for option type"
msgstr ""
#: src/rougail/convert.py:281
msgid "unknown type {0} for {1}"
msgstr ""
#: src/rougail/convert.py:1345
msgid "duplicate dictionary file name {0}"
#: src/rougail/convert.py:413
msgid "family \"{0}\" define multiple time"
msgstr ""
#: src/rougail/convert.py:1392
#: src/rougail/convert.py:681
msgid "variable \"{0}\" define multiple time"
msgstr ""
#: src/rougail/convert.py:787
msgid "params must be a dict for {0}"
msgstr ""
#: src/rougail/convert.py:809
msgid "\"{0}\" has an invalid \"params\" for {1}: {2}"
msgstr ""
#: src/rougail/convert.py:823
msgid "secret_manager must be a dict for {0}"
msgstr ""
#: src/rougail/convert.py:1154
msgid "Cannot execute annotate multiple time"
msgstr ""
#: src/rougail/error.py:67
#: src/rougail/convert.py:1161
msgid "invalid \"structural\" definition ({0}), we cannot load any structural file!"
msgstr ""
#: src/rougail/error.py:67 src/rougail/user_datas.py:251
msgid "{0} in {1}"
msgstr ""
#: src/rougail/object_model.py:50
msgid "unknown boolean value \"{0}\""
msgstr ""
#: src/rougail/object_model.py:182
msgid "cannot find variable \"{0}\" defined in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:186
msgid "the variable \"{0}\" is in fact a family in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:189 src/rougail/object_model.py:473
msgid "unknown object \"{0}\" in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:206
msgid "identifier parameter for \"{0}\" in \"{1}\" cannot be set none dynamic family"
msgstr ""
#: src/rougail/object_model.py:229
msgid "cannot find variable \"{0}\" defined in \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:232
msgid "variable \"{0}\" defined in \"{1}\" for \"{2}\" is a dynamic variable"
msgstr ""
#: src/rougail/object_model.py:246
msgid "the variable \"{0}\" is not a follower, so cannot have index type for param in \"{1}\""
msgstr ""
#: src/rougail/object_model.py:470
msgid "a variable \"{0}\" is needs in attribute \"{1}\" for \"{2}\" but it's a family"
msgstr ""
#: src/rougail/object_model.py:491
msgid "Variable not found \"{0}\" for attribut \"{1}\" in variable \"{2}\""
msgstr ""
#: src/rougail/object_model.py:536
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" is multi but is inside a list"
msgstr ""
#: src/rougail/object_model.py:539
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" is not multi but is not inside a list"
msgstr ""
#: src/rougail/object_model.py:542
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", it's a list"
msgstr ""
#: src/rougail/object_model.py:550
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" is a multi"
msgstr ""
#: src/rougail/object_model.py:572
msgid "\"{0}\" attribut shall not have an \"optional\" attribute for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:584
msgid "variable \"{0}\" has a default value calculated with \"{1}\" which has incompatible type"
msgstr ""
#: src/rougail/object_model.py:620
msgid "\"when\" is not allowed in format version 1.0 for attribute \"{0}\" for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:623
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", \"when\" and \"when_not\" cannot set together"
msgstr ""
#: src/rougail/object_model.py:629
msgid "\"when_not\" is not allowed in format version 1.0 for attribute \"{0}\" for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:682
msgid "cannot find variable \"{0}\" for the information \"{1}\" when calculating \"{2}\""
msgstr ""
#: src/rougail/object_model.py:685
msgid "identifier not allowed for the information \"{0}\" when calculating \"{1}\""
msgstr ""
#: src/rougail/object_model.py:734
msgid "\"when\" is not allowed in format version 1.0 for attribute \"{0}\""
msgstr ""
#: src/rougail/object_model.py:738 src/rougail/object_model.py:746
msgid "the identifier has an invalid attribute \"{0}\", \"when\" and \"when_not\" cannot set together"
msgstr ""
#: src/rougail/object_model.py:768
msgid "the variable \"{0}\" is not a follower, so cannot have index type for \"{1}\""
msgstr ""
#: src/rougail/path.py:206
msgid "A variable or a family located in the \"{0}\" namespace shall not be used in the \"{1}\" namespace"
msgstr ""
#: src/rougail/structural_commandline/annotator.py:67
msgid "alternative_name \"{0}\" conflict with \"--help\""
msgstr ""
@ -95,23 +299,71 @@ msgstr ""
msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\""
msgstr ""
#: src/rougail/structural_commandline/annotator.py:95
#: src/rougail/structural_commandline/annotator.py:98
msgid "negative_description is mandatory for boolean variable, but \"{0}\" hasn't"
msgstr ""
#: src/rougail/structural_commandline/annotator.py:104
#: src/rougail/structural_commandline/annotator.py:106
msgid "negative_description is only available for boolean variable, but \"{0}\" is \"{1}\""
msgstr ""
#: src/rougail/update/update.py:738
msgid "not a XML file: {0}"
#: src/rougail/structural_directory/__init__.py:127
msgid "duplicate dictionary file name {0}"
msgstr ""
#: src/rougail/structural_directory/config.py:32
msgid "Directories where dictionary files are placed"
msgstr ""
#: src/rougail/structural_directory/config.py:46
msgid "Sort dictionaries from differents directories"
msgstr ""
#: src/rougail/structural_directory/config.py:56
msgid "Main namespace name"
msgstr ""
#: src/rougail/structural_directory/config.py:67
msgid "Extra namespaces"
msgstr ""
#: src/rougail/structural_directory/config.py:76
msgid "Extra namespace name"
msgstr ""
#: src/rougail/structural_directory/config.py:82
msgid "Directories where extra dictionary files are placed"
msgstr ""
#: src/rougail/tiramisu.py:167
msgid "cannot calculating \"{0}\" attribute for variable \"{1}\" in {2} with parameters \"{3}\": {4}"
msgstr ""
#: src/rougail/tiramisureflector.py:369
msgid "internal error, {0} is not a dynamic variable"
msgstr ""
#: src/rougail/user_datas.py:116
msgid "cannot load variable path \"{0}\", the identifier \"{1}\" is not valid in {2}"
msgstr ""
#: src/rougail/user_datas.py:171
msgid "the variable \"{0}\" contains secrets and should not be defined in {1}"
msgstr ""
#: src/rougail/user_datas.py:234
msgid "the variable \"{0}\" is a family in {1}"
msgstr ""
#: src/rougail/user_datas.py:249
msgid "{0} loaded from {1}"
msgstr ""
#: src/rougail/utils.py:55
msgid "invalid variable or family name \"{0}\" must only contains lowercase ascii character, number or _"
msgstr ""
#: src/rougail/utils.py:117
#: src/rougail/utils.py:113
msgid "error in jinja \"{0}\" for the variable \"{1}\": {2}"
msgstr ""

View file

@ -4,11 +4,12 @@ requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail"
version = "1.1.1"
version = "1.2.0a18"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "A consistency handling system that was initially designed in the configuration management"
requires-python = ">=3.8"
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
@ -17,6 +18,8 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
@ -40,11 +43,14 @@ dev = [
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail"
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
version_files = [
"src/rougail/__version__.py",
"pyproject.toml:version"
]
update_changelog_on_bump = true
changelog_merge_prerelease = true

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -25,17 +25,15 @@ You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from tiramisu import Config, undefined
from tiramisu.error import PropertiesOptionError, LeadershipError, ConfigError
from tiramisu import Config
from warnings import warn
from typing import List
from re import compile, findall
from .convert import RougailConvert
from .config import RougailConfig
from .update import RougailUpgrade
from .object_model import CONVERT_OPTION
from .utils import normalize_family
from .object_model import CONVERT_OPTION
from .user_datas import UserDatas
from .__version__ import __version__
def tiramisu_display_name(
@ -46,7 +44,7 @@ def tiramisu_display_name(
"""Replace the Tiramisu display_name function to display path + description"""
doc = kls._get_information(subconfig, "doc", None)
comment = f" ({doc})" if doc and doc != kls.impl_getname() else ""
if "{{ identifier }}" in comment:
if "{{ identifier }}" in comment and subconfig.identifiers:
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
path = kls.impl_getpath()
if "{{ identifier }}" in path and subconfig.identifiers:
@ -58,7 +56,7 @@ def tiramisu_display_name(
return f"{path}{comment}"
class Rougail:
class Rougail(UserDatas):
"""Main Rougail object"""
def __init__(
@ -71,18 +69,10 @@ class Rougail:
self.converted = RougailConvert(self.rougailconfig)
self.config = None
def add_path_prefix(
self,
path_prefix: str,
) -> None:
"""Add a prefix"""
self.converted.load_config()
self.converted.parse_directories(path_prefix)
def run(self):
"""Get Tiramisu Config"""
if not self.config:
tiram_obj = self.converted.save(self.rougailconfig["tiramisu_cache"])
tiram_obj = self.converted.save()
optiondescription = {}
custom_types = {
custom.__name__: custom
@ -104,223 +94,5 @@ class Rougail:
)
return self.run()
def user_datas(self, user_datas: List[dict]):
values = {}
errors = []
warnings = []
for datas in user_datas:
options = datas.get("options", {})
for name, data in datas.get("values", {}).items():
values[name] = {
"values": data,
"options": options.copy(),
}
errors.extend(datas.get("errors", []))
warnings.extend(datas.get("warnings", []))
self._auto_configure_dynamics(values)
while values:
value_is_set = False
for option in self._get_variable(self.config):
path = option.path()
if path not in values:
path = path.upper()
options = values.get(path, {}).get("options", {})
if path not in values or options.get("upper") is not True:
continue
else:
options = values[path].get("options", {})
value = values[path]["values"]
if option.ismulti():
if options.get("multi_separator") and not isinstance(value, list):
value = value.split(options["multi_separator"])
values[path]["values"] = value
if options.get("needs_convert"):
value = [convert_value(option, val) for val in value]
values[path]["values"] = value
values[path]["options"]["needs_convert"] = False
elif options.get("needs_convert"):
value = convert_value(option, value)
index = option.index()
if index is not None:
if not isinstance(value, list) or index >= len(value):
continue
value = value[index]
try:
option.value.set(value)
value_is_set = True
if index is not None:
values[path]["values"][index] = undefined
if set(values[path]["values"]) == {undefined}:
values.pop(path)
else:
values.pop(path)
except Exception as err:
if path != option.path():
values[option.path()] = values.pop(path)
if not value_is_set:
break
for path, data in values.items():
try:
option = self.config.option(path)
value = data["values"]
if option.isfollower():
for index, val in enumerate(value):
if val is undefined:
continue
self.config.option(path, index).value.set(val)
else:
option.value.set(value)
except AttributeError as err:
errors.append(str(err))
except (ValueError, LeadershipError) as err:
# errors.append(str(err).replace('"', "'"))
errors.append(str(err))
except PropertiesOptionError as err:
# warnings.append(f'"{err}" but is defined in "{self.filename}"')
warnings.append(str(err))
return {
"errors": errors,
"warnings": warnings,
}
def _get_variable(self, config):
for subconfig in config:
if subconfig.isoptiondescription():
yield from self._get_variable(subconfig)
else:
yield subconfig
def _auto_configure_dynamics(
self,
values,
):
cache = {}
added = []
for path, data in list(values.items()):
value = data["values"]
# for value in data['values'].items():
try:
option = self.config.option(path)
option.name()
except (ConfigError, PropertiesOptionError):
pass
except AttributeError:
config = self.config
current_path = ""
identifiers = []
for name in path.split(".")[:-1]:
if current_path:
current_path += "."
current_path += name
if current_path in cache:
config, identifier = cache[current_path]
identifiers.append(identifier)
else:
tconfig = config.option(name)
try:
tconfig.group_type()
config = tconfig
except AttributeError:
for tconfig in config.list(uncalculated=True):
if tconfig.isdynamic(only_self=True):
identifier = self._get_identifier(
tconfig.name(), name
)
if identifier is None:
continue
dynamic_variable = tconfig.information.get(
"dynamic_variable",
None,
)
if not dynamic_variable:
continue
option_type = self.config.option(
dynamic_variable
).information.get("type")
if identifiers:
for s in identifiers:
dynamic_variable = dynamic_variable.replace(
"{{ identifier }}", str(s), 1
)
if dynamic_variable not in values:
values[dynamic_variable] = {"values": []}
added.append(dynamic_variable)
elif dynamic_variable not in added:
continue
config = tconfig
# option_type = option.information.get('type')
typ = CONVERT_OPTION.get(option_type, {}).get(
"func"
)
if typ:
identifier = typ(identifier)
if (
identifier
not in values[dynamic_variable]["values"]
):
values[dynamic_variable]["values"].append(
identifier
)
identifiers.append(identifier)
cache[current_path] = config, identifier
break
else:
if option.isdynamic():
parent_option = self.config.option(path.rsplit(".", 1)[0])
identifiers = self._get_identifier(
parent_option.name(uncalculated=True),
parent_option.name(),
)
dynamic_variable = None
while True:
dynamic_variable = parent_option.information.get(
"dynamic_variable",
None,
)
if dynamic_variable:
break
parent_option = self.config.option(
parent_option.path().rsplit(".", 1)[0]
)
if "." not in parent_option.path():
parent_option = None
break
if not parent_option:
continue
identifiers = parent_option.identifiers()
for identifier in identifiers:
dynamic_variable = dynamic_variable.replace(
"{{ identifier }}", str(identifier), 1
)
if dynamic_variable not in values:
values[dynamic_variable] = {"values": []}
added.append(dynamic_variable)
elif dynamic_variable not in added:
continue
option_type = option.information.get("type")
typ = CONVERT_OPTION.get(option_type, {}).get("func")
if typ:
identifier = typ(identifier)
if identifier not in values[dynamic_variable]["values"]:
values[dynamic_variable]["values"].append(identifier)
cache[option.path()] = option, identifier
def _get_identifier(self, true_name, name) -> str:
regexp = true_name.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
return
return finded[0]
def convert_value(option, value):
if value == "":
return None
option_type = option.information.get("type")
func = CONVERT_OPTION.get(option_type, {}).get("func")
if func:
return func(value)
return value
__all__ = ("Rougail", "RougailConfig", "RougailUpgrade")
__all__ = ("Rougail", "RougailConfig")

View file

@ -0,0 +1 @@
__version__ = "1.2.0a18"

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -71,16 +71,38 @@ class SpaceAnnotator: # pylint: disable=R0903
if extra_annotator in ANNOTATORS:
continue
get_annotators(ANNOTATORS, extra_annotator)
for plugin in objectspace.plugins:
for structural in objectspace.structurals:
try:
get_annotators(ANNOTATORS, f"rougail.{plugin}", "annotator")
get_annotators(
ANNOTATORS, f"rougail.structural_{structural}", "annotator"
)
except ModuleNotFoundError:
pass
for user_data in objectspace.user_datas:
try:
get_annotators(
ANNOTATORS, f"rougail.user_data_{user_data}", "annotator"
)
except ModuleNotFoundError:
pass
if objectspace.output:
try:
get_annotators(
ANNOTATORS, f"rougail.output_{objectspace.output}", "annotator"
)
except ModuleNotFoundError:
pass
annotators = ANNOTATORS["rougail.annotator"].copy()
for extra_annotator in objectspace.extra_annotators:
annotators.extend(ANNOTATORS[extra_annotator])
for plugin in objectspace.plugins:
annotators.extend(ANNOTATORS[f"rougail.{plugin}.annotator"])
for structural in objectspace.structurals:
annotators.extend(ANNOTATORS[f"rougail.structural_{structural}.annotator"])
for user_data in objectspace.user_datas:
annotators.extend(ANNOTATORS[f"rougail.user_data_{user_data}.annotator"])
if objectspace.output:
annotators.extend(
ANNOTATORS[f"rougail.output_{objectspace.output}.annotator"]
)
annotators = sorted(annotators, key=get_level)
functions = {}
functions_files = objectspace.functions_files

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -125,9 +125,19 @@ class Annotator(Walk):
if family.type == "dynamic" and isinstance(
family.dynamic, VariableCalculation
):
path = self.objectspace.paths.get_full_path(
family.dynamic.variable,
family.path,
path = family.dynamic.variable
if (
family.version != "1.0"
and self.objectspace.paths.regexp_relative.search(path)
):
path = self.objectspace.paths.get_full_path(
family.dynamic.variable,
family.path,
)
if family.version == "1.0" and "{{ suffix }}" in path:
path = path.replace("{{ suffix }}", "{{ identifier }}")
self.objectspace.dynamics_variable.setdefault(path, []).append(
family.path
)
self.objectspace.informations.add(family.path, "dynamic_variable", path)
@ -333,7 +343,7 @@ class Annotator(Walk):
if self._has_mode(variable):
msg = _(
'the variable "{0}" is in "{1}" mode but family has the higher family mode "{2}"'
).format(variable.name, variable_mode, family_mode)
).format(variable.path, variable_mode, family_mode)
raise DictConsistencyError(msg, 61, variable.xmlfiles)
self._set_auto_mode(variable, family_mode)
if not variable.mode:

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -120,7 +120,7 @@ class Annotator(Walk):
else:
value = []
for calculation in frozen:
calculation_copy = calculation.copy()
calculation_copy = calculation.model_copy()
calculation_copy.attribute_name = "frozen"
calculation_copy.ori_path = calculation_copy.path
calculation_copy.path = path

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -47,6 +47,7 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace = objectspace
self.convert_value()
self.valid_choices()
self.valid_regexp()
def convert_value(self) -> None:
"""convert value"""
@ -75,8 +76,8 @@ class Annotator(Walk): # pylint: disable=R0903
raise DictConsistencyError(msg, 68, variable.xmlfiles)
if variable.path in self.objectspace.followers and multi != "submulti":
msg = _(
'the follower "{0}" without multi attribute can only have one value'
).format(variable.name)
'the follower "{0}" is not multi, so cannot have a list has default value'
).format(variable.path)
raise DictConsistencyError(msg, 87, variable.xmlfiles)
if not variable.default:
variable.default = None
@ -92,7 +93,7 @@ class Annotator(Walk): # pylint: disable=R0903
elif variable.multi:
msg = _(
'the variable "{0}" is multi but has a non list default value'
).format(variable.name)
).format(variable.path)
raise DictConsistencyError(msg, 12, variable.xmlfiles)
elif variable.path in self.objectspace.followers:
self.objectspace.default_multi[variable.path] = variable.default
@ -114,7 +115,7 @@ class Annotator(Walk): # pylint: disable=R0903
if isinstance(variable.choices, Calculation):
continue
if variable.choices is None:
msg = f'the variable "{variable.path}" is a "choice" variable but don\'t have any choice'
msg = _('the variable "{0}" is a "choice" variable but don\'t have any choice').format(variable.path)
raise DictConsistencyError(msg, 19, variable.xmlfiles)
if not variable.mandatory and not variable.multi:
self.add_choice_nil(variable)
@ -125,3 +126,11 @@ class Annotator(Walk): # pylint: disable=R0903
if choice is None:
return
variable.choices.append(None)
def valid_regexp(self) -> None:
for variable in self.get_variables():
if variable.type != "regexp":
continue
if variable.regexp is None:
msg = _('the variable "{0}" is a "regexp" variable but don\'t have any regexp').format(variable.path)
raise DictConsistencyError(msg, 66, variable.xmlfiles)

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -25,10 +25,11 @@ You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from tiramisu.error import display_list
from rougail.i18n import _
from rougail.utils import calc_multi_for_type_variable
from rougail.error import DictConsistencyError
from rougail.object_model import Calculation, VariableCalculation
from tiramisu.error import display_list
class Walk:
@ -62,12 +63,6 @@ class Annotator(Walk): # pylint: disable=R0903
if not objectspace.paths:
return
self.objectspace = objectspace
if self.objectspace.main_namespace:
self.forbidden_name = [self.objectspace.main_namespace]
for extra in self.objectspace.extra_dictionaries:
self.forbidden_name.append(extra)
else:
self.forbidden_name = []
# default type inference from a default value with :term:`basic types`
self.basic_types = {
str: "string",
@ -75,29 +70,47 @@ class Annotator(Walk): # pylint: disable=R0903
bool: "boolean",
float: "float",
}
self.verify_secret_managers()
self.verify_choices()
self.convert_variable()
self.convert_test()
self.convert_examples()
self.convert_help()
def verify_secret_managers(self):
for variable in self.get_variables():
if not variable.secret_manager:
continue
path = variable.path
if variable.type not in ["unix_user", "secret"]:
msg = _('only "unix_user" or "secret" variable type can have "secret_manager" attribute, but "{0}" has type "{1}"')
raise DictConsistencyError(msg.format(path, variable.type), 56, variable.xmlfiles)
if variable.multi and path not in self.objectspace.leaders:
msg = _('the variable "{0}" has attribute "secret_manager" but is a multi variable')
raise DictConsistencyError(msg.format(path), 57, variable.xmlfiles)
if variable.default is not None:
msg = _('the variable "{0}" has attribute "secret_manager" so must not have default value')
raise DictConsistencyError(msg.format(path), 59, variable.xmlfiles)
def convert_variable(self):
"""convert variable"""
for variable in self.get_variables():
if variable.version != "1.0":
if variable.type == "symlink":
continue
self._convert_variable_inference(variable)
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.version != "1.0":
self._default_variable_copy_informations(variable)
if variable.multi is None:
variable.multi = False
if variable.type is None:
variable.type = "string"
self.objectspace.informations.add(variable.path, "type", variable.type)
self._convert_variable_inference(variable)
self._convert_variable_multi(variable)
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.version != '1.0' and isinstance(variable.default, VariableCalculation):
calculated_variable_path, calculated_variable, identifier = variable.default.get_variable(
self.objectspace
)
else:
calculated_variable = None
if variable.version != "1.0" and calculated_variable is not None:
self._default_variable_copy_informations(variable, calculated_variable)
self._convert_variable(variable)
def _convert_variable_inference(
@ -105,62 +118,59 @@ class Annotator(Walk): # pylint: disable=R0903
variable,
) -> None:
# variable has no type
if variable.type is None:
# choice type inference from the `choices` attribute
if variable.choices is not None:
variable.type = "choice"
elif variable.regexp is not None:
variable.type = "regexp"
elif variable.default not in [None, []]:
if isinstance(variable.default, list):
tested_value = variable.default[0]
else:
tested_value = variable.default
variable.type = self.basic_types.get(type(tested_value), None)
# variable has no multi attribute
if variable.multi is None and not (
variable.type is None and isinstance(variable.default, VariableCalculation)
):
if variable.path in self.objectspace.leaders:
variable.multi = True
if variable.type is not None:
return
# choice type inference from the `choices` attribute
if variable.choices is not None:
variable.type = "choice"
elif variable.regexp is not None:
variable.type = "regexp"
elif variable.default not in [None, []]:
if isinstance(variable.default, list):
tested_value = variable.default[0]
else:
variable.multi = isinstance(variable.default, list)
tested_value = variable.default
variable.type = self.basic_types.get(type(tested_value), None)
def _convert_variable_multi(
self,
variable,
) -> None:
# variable has no multi attribute
if variable.multi is not None:
return
if variable.path in self.objectspace.leaders:
variable.multi = self.objectspace.multis[variable.path] = True
elif variable.version != "1.0" and isinstance(variable.default, VariableCalculation):
# FIXME
calculated_variable_path, calculated_variable, identifier = variable.default.get_variable(
self.objectspace
)
if calculated_variable is not None:
if calculated_variable.multi is None:
self._convert_variable_multi(calculated_variable)
variable.multi = calc_multi_for_type_variable(variable, calculated_variable_path, calculated_variable, self.objectspace)[1]
if calculated_variable.path in self.objectspace.followers and variable.mandatory is calculated_variable.mandatory is False and calculated_variable.path.rsplit(".", 1)[0] != variable.path.rsplit(".", 1)[0]:
variable.empty = False
else:
variable.multi = isinstance(variable.default, list)
def _default_variable_copy_informations(
self,
variable,
calculated_variable,
) -> None:
# if a variable has a variable as default value, that means the type/params or multi should has same value
if variable.type is not None or not isinstance(
variable.default, VariableCalculation
):
if not isinstance(variable.default, VariableCalculation) or variable.type is not None:
return
# copy type and params
calculated_variable_path = variable.default.variable
calculated_variable, identifier = self.objectspace.paths.get_with_dynamic(
calculated_variable_path,
variable.default.path_prefix,
variable.path,
variable.version,
variable.namespace,
variable.xmlfiles,
)
if calculated_variable is None:
return
variable.type = calculated_variable.type
if variable.params is None and calculated_variable.params is not None:
variable.params = calculated_variable.params
# copy multi attribut
if variable.multi is None:
calculated_path = calculated_variable.path
if (
calculated_path in self.objectspace.leaders
and variable.path in self.objectspace.followers
and calculated_path.rsplit(".")[0] == variable.path.rsplit(".")[0]
):
variable.multi = False
else:
variable.multi = calculated_variable.multi
if variable.type == 'choice' and variable.choices is None:
variable.choices = calculated_variable.choices
if variable.type == 'regexp' and variable.regexp is None:
variable.regexp = calculated_variable.regexp
def _convert_variable(
self,
@ -169,6 +179,9 @@ class Annotator(Walk): # pylint: disable=R0903
# variable without description: description is the name
if not variable.description:
variable.description = variable.name
if variable.type is None:
variable.type = "string"
self.objectspace.informations.add(variable.path, "type", variable.type)
if variable.path in self.objectspace.followers:
if not variable.multi:
self.objectspace.multis[variable.path] = True
@ -177,8 +190,6 @@ class Annotator(Walk): # pylint: disable=R0903
elif variable.multi:
self.objectspace.multis[variable.path] = True
if variable.path in self.objectspace.leaders:
if not self.objectspace.multis.get(variable.path, False):
variable.multi = self.objectspace.multis[variable.path] = True
family = self.objectspace.paths[variable.path.rsplit(".", 1)[0]]
if variable.hidden:
family.hidden = variable.hidden

1
src/rougail/cli Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-cli/src/rougail/cli

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -26,12 +26,17 @@ You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from sys import version_info
from pathlib import Path
from tiramisu import Config
from ruamel.yaml import YAML
from .utils import _, load_modules, normalize_family
from .convert import RougailConvert
from .object_model import get_convert_option_types
if version_info.major == 3 and version_info.minor:
import rougail.structural_commandline.object_model
RENAMED = {
"dictionaries_dir": "main_dictionaries",
@ -60,43 +65,63 @@ def get_sub_modules():
def get_level(module):
return module["level"]
return float(module["level"]) + {
"structural": 0.1,
"user data": 0.2,
"output": 0.3,
}.get(module["process"])
class _RougailConfig:
def __init__(self, backward_compatibility: bool, root, extra_vars: dict):
def __init__(self, backward_compatibility: bool, add_extra_options: bool):
self.backward_compatibility = backward_compatibility
self.add_extra_options = add_extra_options
self.root = None
def copy(self):
rougailconfig = _RougailConfig(
self.backward_compatibility,
self.add_extra_options,
)
if self.root:
rougailconfig.config.value.importation(self.config.value.exportation())
rougailconfig.config.property.importation(
self.config.property.exportation()
)
rougailconfig.config.property.read_only()
rougailconfig.root = self.root
rougailconfig.config = self.config
rougailconfig.extra_vars = self.extra_vars
for variable in self.not_in_tiramisu:
value = getattr(self, variable)
if not isinstance(value, str):
value = value.copy()
setattr(rougailconfig, variable, value)
return rougailconfig
def generate_config(self):
root, extra_vars = _rougail_config(
self.backward_compatibility, self.add_extra_options
)
self.root = root
self.config = Config(
self.root,
)
self.config.property.read_only()
self.extra_vars = extra_vars
self.not_in_tiramisu = NOT_IN_TIRAMISU | extra_vars
for variable, default_value in self.not_in_tiramisu.items():
if not isinstance(default_value, str):
default_value = default_value.copy()
setattr(self, variable, default_value)
def copy(self):
rougailconfig = _RougailConfig(
self.backward_compatibility, self.root, self.extra_vars
)
rougailconfig.config.value.importation(self.config.value.exportation())
rougailconfig.config.property.importation(self.config.property.exportation())
rougailconfig.config.property.read_only()
for variable in self.not_in_tiramisu:
value = getattr(self, variable)
if not isinstance(value, str):
value = value.copy()
setattr(rougailconfig, variable, value)
return rougailconfig
self.config.property.read_only()
def __setitem__(
self,
key,
value,
) -> None:
if self.root is None:
self.generate_config()
if key in self.not_in_tiramisu:
setattr(self, key, value)
else:
@ -122,6 +147,8 @@ class _RougailConfig:
self,
key,
) -> None:
if self.root is None:
self.generate_config()
if key in self.not_in_tiramisu:
return getattr(self, key)
if key == "export_with_import":
@ -182,19 +209,19 @@ class FakeRougailConvert(RougailConvert):
self.base_option_name = "baseoption"
self.export_with_import = True
self.internal_functions = []
self.plugins = ["structural_commandline"]
self.force_optional = False
self.structurals = ["commandline"]
self.user_datas = []
self.output = None
self.add_extra_options = self.add_extra_options
self.tiramisu_cache = False
self.load_unexist_redefine = False
def get_rougail_config(
*,
def _rougail_config(
backward_compatibility: bool = True,
add_extra_options: bool = True,
) -> _RougailConfig:
if backward_compatibility:
main_namespace_default = "rougail"
else:
main_namespace_default = "null"
) -> "OptionDescription":
rougail_options = f"""default_dictionary_format_version:
description: Dictionary format version by default, if not specified in dictionary file
alternative_name: v
@ -203,82 +230,8 @@ def get_rougail_config(
- '1.1'
mandatory: false
main_dictionaries:
description: 'Directories where dictionary files are placed'
type: unix_filename
alternative_name: m
params:
allow_relative: True
test_existence: True
types:
- directory
multi: true
sort_dictionaries_all:
description: Sort dictionaries from differents directories
negative_description: Sort dictionaries directory by directory
default: false
main_namespace:
description: Main namespace name
default: {main_namespace_default}
alternative_name: s
mandatory: false
extra_dictionaries:
description: Extra namespaces
type: leadership
disabled:
variable: main_namespace
when: null
names:
description: 'Extra namespace name'
alternative_name: xn
multi: true
mandatory: false
directories:
description: Directories where extra dictionary files are placed
alternative_name: xd
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- directory
multi: true
upgrade:
description: Update dictionaries to newest Rougail format version
negative_description: Do not update dictionaries to newest Rougail format version
default: false
upgrade_options:
description: Update informations
disabled:
variable: upgrade
when: false
main_dictionaries:
description: 'Directories where dictionary files will be placed'
default:
variable: __.main_dictionaries
extra_dictionary:
description: 'Directories where extra files will be placed'
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- directory
disabled:
variable: __.main_namespace
when: null
functions_files:
description: File with functions
description: {_("File with functions")}
alternative_name: c
type: unix_filename
params:
@ -290,74 +243,74 @@ functions_files:
mandatory: false
modes_level:
description: All modes level available
description: {_("All modes level available")}
multi: true
mandatory: false
"""
if backward_compatibility:
rougail_options += """
default:
rougail_options += """ default:
- basic
- standard
- advanced
"""
rougail_options += """
rougail_options += f"""
default_family_mode:
description: Default mode for a family
description: {_("Default mode for a family")}
default:
jinja: |
{% if modes_level %}
{{ modes_level[0] }}
{% endif %}
{{% if modes_level %}}
{{{{ modes_level[0] }}}}
{{% endif %}}
disabled:
jinja: |
{% if not modes_level %}
{{% if not modes_level %}}
No mode
{% endif %}
{{% endif %}}
validators:
- type: jinja
jinja: |
{% if default_family_mode not in modes_level %}
not in modes_level ({modes_level})
{% endif %}
{{% if default_family_mode not in modes_level %}}
not in modes_level ({{modes_level}})
{{% endif %}}
commandline: false
default_variable_mode:
description: Default mode for a variable
description: {_("Default mode for a variable")}
default:
jinja: |
{% if modes_level %}
{% if modes_level | length == 1 %}
{{ modes_level[0] }}
{% else %}
{{ modes_level[1] }}
{% endif %}
{% endif %}
{{% if modes_level %}}
{{% if modes_level | length == 1 %}}
{{{{ modes_level[0] }}}}
{{% else %}}
{{{{ modes_level[1] }}}}
{{% endif %}}
{{% endif %}}
disabled:
jinja: |
{% if not modes_level %}
{{% if not modes_level %}}
No mode
{% endif %}
{{% endif %}}
validators:
- type: jinja
jinja: |
{% if default_variable_mode not in modes_level %}
not in modes_level ({modes_level})
{% endif %}
{{% if default_variable_mode not in modes_level %}}
not in modes_level ({{modes_level}})
{{% endif %}}
commandline: false
base_option_name:
description: Option name for the base option
description: {_("Option name for the base option")}
default: baseoption
commandline: false
not_export_with_import:
description: In cache file, do not importation of Tiramisu and other dependencies
description: {_("In cache file, do not importation of Tiramisu and other dependencies")}
negative_description: {_("In cache file, do importation of Tiramisu and other dependencies")}
default: false
commandline: false
tiramisu_cache:
description: Tiramisu cache filename
description: {_("Tiramisu cache filename")}
alternative_name: t
type: unix_filename
mandatory: false
@ -365,77 +318,120 @@ tiramisu_cache:
allow_relative: true
internal_functions:
description: Name of internal functions that we can use as a function
description: {_("Name of internal functions that we can use as a function")}
multi: true
mandatory: false
commandline: false
extra_annotators:
description: Name of extra annotators
multi: true
mandatory: false
commandline: false
plugins:
description: Name of Rougail plugins
description: {_("Name of extra annotators")}
multi: true
mandatory: false
commandline: false
suffix:
description: Suffix add to generated option name
description: {_("Suffix add to generated options name")}
default: ''
mandatory: false
commandline: false
force_optional:
description: {_("Every variables in calculation are optionals")}
negative_description: {_("Variables in calculation are not optional by default")}
default: False
load_unexist_redefine:
description: {_("Loads redefine variables even if there don't already exists")}
negative_description: {_("Loads redefine variables even if there don't already exists")}
commandline: false
default: False
secret_manager:
pattern:
description: {_("The secret pattern to build item name in Bitwarden")}
help: {_("The pattern is in Jinja format")}
default: "{{{{ project }}}} - {{{{ environment }}}} - {{{{ service }}}} - {{{{ user }}}}"
"""
processes = {
"structural": [],
"output": [],
"user data": [],
"output": [],
}
processes_empty = []
for module in get_sub_modules().values():
data = module.get_rougail_config()
processes[data["process"]].append(data)
data = module.get_rougail_config(backward_compatibility=backward_compatibility)
if data["process"]:
processes[data["process"]].append(data)
else:
processes_empty.append(data["options"])
# reorder
for process in processes:
processes[process] = list(sorted(processes[process], key=get_level))
rougail_process = """step: # Load and exporter steps
disabled:
variable: upgrade"""
rougail_process = "step: # Load and exporter steps"
for process in processes:
if processes[process]:
objects = processes[process]
rougail_process += """
{NAME}:
description: Select for {NAME}
alternative_name: {NAME[0]}
choices:
""".format(
NAME=normalize_family(process),
)
if process != "structural":
rougail_process += """
alternative_name: {NAME[0]}
""".format(
NAME=normalize_family(process),
)
rougail_process += """
choices:
"""
for obj in objects:
rougail_process += f" - {obj['name']}\n"
if process == "structural":
rougail_process += " commandline: false"
rougail_process += """ commandline: false
multi: true
default:
- directory
"""
hidden_outputs = [
process["name"]
for process in processes["output"]
if process.get("allow_user_data", True)
]
if hidden_outputs:
rougail_process += """
hidden:
type: jinja
jinja: |
"""
for hidden_output in hidden_outputs:
rougail_process += """ {% if _.output is not propertyerror and _.output == 'NAME' %}
Cannot load structural for NAME output
{% endif %}
""".replace("NAME", hidden_output
)
elif process == "user data":
rougail_process += """ multi: true
mandatory: false
"""
mandatory: false"""
hidden_outputs = [
process["name"]
for process in processes["output"]
if not process.get("allow_user_data", True)
]
if hidden_outputs:
rougail_process += """ hidden:
rougail_process += """
hidden:
type: jinja
jinja: |
"""
for hidden_output in hidden_outputs:
rougail_process += """ {% if _.output == 'NAME' %}
rougail_process += """ {% if _.output is not propertyerror and _.output == 'NAME' %}
Cannot load user data for NAME output
{% endif %}
""".replace(
{% endif %}""".replace(
"NAME", hidden_output
)
elif objects:
@ -443,10 +439,10 @@ suffix:
DEFAULT=objects[0]["name"]
)
else:
if process == 'output':
prop = 'hidden'
if process == "output":
prop = "hidden"
else:
prop = 'disabled'
prop = "disabled"
rougail_process += """
{NAME}:
description: Select for {NAME}
@ -460,9 +456,26 @@ suffix:
NAME=normalize_family(process),
PROP=prop,
)
rougail_process += f"""
default_params:
description: {_("Default parameters for option type")}
"""
for typ, params in get_convert_option_types():
rougail_process += f"""
{typ}:
"""
for key, key_type, multi, value in params:
rougail_process += f"""
{key}:
type: {key_type}
multi: {multi}
mandatory: false
default: {value}
"""
rougail_options += rougail_process
convert = FakeRougailConvert(add_extra_options)
convert._init()
convert.init()
convert.namespace = None
convert.parse_root_file(
"rougail.config",
@ -470,27 +483,48 @@ suffix:
"1.1",
YAML().load(rougail_options),
)
for process_empty in processes_empty:
convert.parse_root_file(
"rougail.config",
"",
"1.1",
YAML().load(process_empty),
)
extra_vars = {}
for process in processes:
for obj in processes[process]:
if "extra_vars" in obj:
extra_vars |= obj["extra_vars"]
if not "options" in obj:
continue
objects = []
for obj in sorted(
[obj for objects in processes.values() for obj in objects], key=get_level
):
if "extra_vars" in obj:
extra_vars |= obj["extra_vars"]
if not "options" in obj:
continue
if not isinstance(obj["options"], list):
options = [obj["options"]]
else:
options = obj["options"]
for option in options:
convert.parse_root_file(
f'rougail.config.{obj["name"]}',
"",
"1.1",
YAML().load(obj["options"]),
YAML().load(option),
)
tiram_obj = convert.save(None)
tiram_obj = convert.save()
optiondescription = {}
exec(tiram_obj, {}, optiondescription) # pylint: disable=W0122
return optiondescription["option_0"], extra_vars
def get_rougail_config(
*,
backward_compatibility: bool = True,
add_extra_options: bool = True,
) -> _RougailConfig:
return _RougailConfig(
backward_compatibility,
optiondescription["option_0"],
extra_vars=extra_vars,
add_extra_options,
)

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -27,9 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
from itertools import chain
from pathlib import Path
from re import compile, findall
from typing import (
Any,
Dict,
@ -42,8 +40,6 @@ from typing import (
get_type_hints,
)
from pydantic import ValidationError
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
from pydantic import ValidationError
@ -68,7 +64,8 @@ from .object_model import (
VariableCalculation,
)
from .tiramisureflector import TiramisuReflector
from .utils import get_realpath, normalize_family, load_modules
from .utils import load_modules
from .path import Paths
from .error import DictConsistencyError
property_types = Union[Literal[True], Calculation]
@ -87,6 +84,12 @@ class Property:
) -> None:
self._properties.setdefault(path, {})[property_] = value
def get(self, path: str) -> None:
return self._properties.setdefault(path, {})
def remove(self, path: str, property_: str) -> None:
del self._properties[path][property_]
def __getitem__(
self,
path: str,
@ -100,218 +103,6 @@ class Property:
return path in self._properties
class Paths:
regexp_relative = compile(r"^_*\.(.*)$")
def __init__(
self,
default_namespace: str,
) -> None:
self._data: Dict[str, Union[Variable, Family]] = {}
self._dynamics: Dict[str:str] = {}
if default_namespace is not None:
default_namespace = normalize_family(default_namespace)
self.default_namespace = default_namespace
self.path_prefix = None
def has_value(self) -> bool:
return self._data != {}
def add(
self,
path: str,
data: Any,
is_dynamic: bool,
dynamic: str,
*,
force: bool = False,
) -> None:
self._data[path] = data
if not force and is_dynamic:
self._dynamics[path] = dynamic
def get_full_path(
self,
path: str,
current_path: str,
):
relative, subpath = path.split(".", 1)
relative_len = len(relative)
path_len = current_path.count(".")
if path_len + 1 == relative_len:
return subpath
parent_path = current_path.rsplit(".", relative_len)[0]
return parent_path + "." + subpath
def get_with_dynamic(
self,
path: str,
identifier_path: str,
current_path: str,
version: str,
namespace: str,
xmlfiles: List[str],
) -> Any:
identifier = None
if version != "1.0" and self.regexp_relative.search(path):
path = self.get_full_path(
path,
current_path,
)
else:
path = get_realpath(path, identifier_path)
dynamic = None
# version 1.0
if version == "1.0":
if not path in self._data and "{{ suffix }}" not in path:
new_path = None
current_path = None
identifiers = []
for name in path.split("."):
parent_path = current_path
if current_path:
current_path += "." + name
else:
current_path = name
if current_path in self._data:
if new_path:
new_path += "." + name
else:
new_path = name
continue
for dynamic_path in self._dynamics:
if "." in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
parent_dynamic == parent_path
and name_dynamic.endswith("{{ identifier }}")
and name == name_dynamic.replace("{{ identifier }}", "")
):
new_path += "." + name_dynamic
break
regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
continue
if finded[0] == "{{ identifier }}":
identifiers.append(None)
else:
identifiers.append(finded[0])
if new_path is None:
new_path = name_dynamic
else:
new_path += "." + name_dynamic
parent_path = dynamic_path
break
else:
if new_path:
new_path += "." + name
else:
new_path = name
path = new_path
else:
identifiers = None
if "{{ suffix }}" in path:
path = path.replace("{{ suffix }}", "{{ identifier }}")
elif not path in self._data:
current_path = None
parent_path = None
new_path = current_path
identifiers = []
for name in path.split("."):
if current_path:
current_path += "." + name
else:
current_path = name
# parent_path, name_path = path.rsplit('.', 1)
if current_path in self._data:
if new_path:
new_path += "." + name
else:
new_path = name
parent_path = current_path
continue
for dynamic_path in self._dynamics:
if "." in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
"{{ identifier }}" not in name_dynamic
or parent_path != parent_dynamic
):
continue
regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
continue
if finded[0] == "{{ identifier }}":
identifiers.append(None)
else:
identifiers.append(finded[0])
if new_path is None:
new_path = name_dynamic
else:
new_path += "." + name_dynamic
parent_path = dynamic_path
break
else:
if new_path:
new_path += "." + name
else:
new_path = name
if "{{ identifier }}" in name:
identifiers.append(None)
parent_path = current_path
path = new_path
else:
identifiers = None
if path not in self._data:
return None, None
option = self._data[path]
option_namespace = option.namespace
if (
self.default_namespace not in [namespace, option_namespace]
and namespace != option_namespace
):
msg = _(
'A variable or a family located in the "{0}" namespace shall not be used in the "{1}" namespace'
).format(option_namespace, namespace)
raise DictConsistencyError(msg, 38, xmlfiles)
return option, identifiers
def __getitem__(
self,
path: str,
) -> Union[Family, Variable]:
if not path in self._data:
raise AttributeError(f"cannot find variable or family {path}")
return self._data[path]
def __contains__(
self,
path: str,
) -> bool:
return path in self._data
def __delitem__(
self,
path: str,
) -> None:
logging.info("remove empty family %s", path)
del self._data[path]
def is_dynamic(self, path: str) -> bool:
return path in self._dynamics
def get(self):
return self._data.values()
information_types = Dict[str, Union[str, int, float, bool]]
@ -342,7 +133,7 @@ class ParserVariable:
def __init__(self, rougailconfig):
self.rougailconfig = rougailconfig
self.load_config()
self.paths = Paths(self.main_namespace)
self.paths = Paths(None)
self.families = []
self.variables = []
self.parents = {".": []}
@ -350,6 +141,7 @@ class ParserVariable:
self.reflector_names = {}
self.leaders = []
self.followers = []
self.dynamics_variable = {}
self.multis = {}
self.default_multi = {}
self.jinja = {}
@ -362,20 +154,11 @@ class ParserVariable:
self.properties = Property()
# self.choices = Appendable()
self.has_dyn_option = False
self.path_prefix = None
self.is_init = False
super().__init__()
def load_config(self) -> None:
rougailconfig = self.rougailconfig
self.sort_dictionaries_all = rougailconfig["sort_dictionaries_all"]
try:
self.main_dictionaries = rougailconfig["main_dictionaries"]
except:
self.main_dictionaries = []
self.main_namespace = rougailconfig["main_namespace"]
if self.main_namespace:
self.extra_dictionaries = rougailconfig["extra_dictionaries"]
self.suffix = rougailconfig["suffix"]
self.default_dictionary_format_version = rougailconfig[
"default_dictionary_format_version"
@ -390,35 +173,58 @@ class ParserVariable:
self.base_option_name = rougailconfig["base_option_name"]
self.export_with_import = rougailconfig["export_with_import"]
self.internal_functions = rougailconfig["internal_functions"]
self.force_optional = rougailconfig["force_optional"]
self.add_extra_options = rougailconfig[
"structural_commandline.add_extra_options"
]
self.plugins = rougailconfig["plugins"]
self.structurals = rougailconfig["step.structural"]
try:
self.user_datas = rougailconfig["step.user_data"]
except:
self.user_datas = []
try:
self.output = rougailconfig["step.output"]
except:
self.output = None
self.tiramisu_cache = rougailconfig["tiramisu_cache"]
self.load_unexist_redefine = rougailconfig["load_unexist_redefine"]
self.secret_pattern = rougailconfig['secret_manager.pattern']
# change default initkwargs in CONVERT_OPTION
if hasattr(rougailconfig, "config"):
for sub_od in rougailconfig.config.option("default_params"):
for option in sub_od:
if option.owner.isdefault():
continue
convert_option = CONVERT_OPTION[sub_od.name()]
if "initkwargs" not in convert_option:
convert_option["initkwargs"] = {}
convert_option["initkwargs"][option.name()] = option.value.get()
def _init(self):
def init(self):
if self.is_init:
return
variable = Variable
family = Family
if self.plugins:
root = Path(__file__).parent
for plugin in self.plugins:
module_path = root / plugin / "object_model.py"
if not module_path.is_file():
continue
module = load_modules(
f"rougail.{plugin}.object_model", str(module_path)
root = Path(__file__).parent
self.walker = None
for structural_name in self.structurals:
structural = f"structural_{structural_name}"
module_path = root / structural / "__init__.py"
if not module_path.is_file():
continue
module = load_modules(f"rougail.{structural}", str(module_path))
if "Variable" in module.__all__:
variable = type(
variable.__name__ + "_" + structural,
(variable, module.Variable),
{},
)
if "Variable" in module.__all__:
variable = type(
variable.__name__ + "_" + plugin,
(variable, module.Variable),
{},
)
if "Family" in module.__all__:
family = type(
family.__name__ + "_" + plugin, (family, module.Family), {}
)
if "Family" in module.__all__:
family = type(
family.__name__ + "_" + structural, (family, module.Family), {}
)
if not self.walker and "Walker" in module.__all__:
self.walker = module.Walker
self.variable = variable
self.family = family
self.dynamic = type(Dynamic.__name__, (Dynamic, family), {})
@ -426,7 +232,7 @@ class ParserVariable:
# FIXME: only for format 1.0
self.family_types = hint["type"].__args__ # pylint: disable=W0201
self.family_attrs = frozenset( # pylint: disable=W0201
set(hint) - {"name", "path", "xmlfiles"} | {"redefine"}
set(hint) - {"name", "path", "xmlfiles"} | {"redefine", "exists"}
)
self.family_calculations = self.search_calculation( # pylint: disable=W0201
hint
@ -588,7 +394,11 @@ class ParserVariable:
return
family_obj = {}
subfamily_obj = {}
force_to_attrs = list(self.list_attributes(obj))
if version != "1.0":
exists = obj.pop("exists", None)
else:
exists = None
force_to_attrs = list(self.list_attributes(obj, filename))
for key, value in obj.items():
if key in force_to_attrs:
if key.startswith("_"):
@ -599,11 +409,10 @@ class ParserVariable:
if path in self.paths:
# it's just for modify subfamily or subvariable, do not redefine
if family_obj:
if not obj.pop("redefine", False):
if exists in [None, True] and not obj.pop("redefine", False):
msg = _('family "{0}" define multiple time').format(path)
raise DictConsistencyError(
f'The family "{path}" already exists and it is not redefined',
32,
[filename],
msg, 32, self.paths[path].xmlfiles + [filename]
)
# convert to Calculation objects
self.parse_parameters(
@ -615,23 +424,27 @@ class ParserVariable:
version,
typ="family",
)
self.paths.add(
path,
self.paths[path].model_copy(update=obj),
family_is_dynamic,
parent_dynamic,
force=True,
)
if self.load_unexist_redefine or exists in [None, True]:
self.paths.add(
path,
self.paths[path].model_copy(update=obj),
family_is_dynamic,
parent_dynamic,
force=True,
)
self.paths[path].xmlfiles.append(filename)
force_not_first = True
if self.paths[path].type == "dynamic":
family_is_dynamic = True
parent_dynamic = path
else:
if "redefine" in obj and obj["redefine"]:
redefine = family_obj.pop("redefine", None)
if not self.load_unexist_redefine and exists is None and redefine:
raise Exception(
f'cannot redefine the inexisting family "{path}" in {filename}'
)
if not self.load_unexist_redefine and exists is True:
return
extra_attrs = set(family_obj) - self.family_attrs
if extra_attrs:
raise Exception(f"extra attrs ... {extra_attrs}")
@ -693,10 +506,17 @@ class ParserVariable:
def list_attributes(
self,
obj: Dict[str, Any],
filename: str,
) -> Iterator[str]:
"""List attributes"""
force_to_variable = []
for key, value in obj.items():
if not isinstance(key, str):
raise DictConsistencyError(
f'a key is not in string format: {key}',
103,
[filename],
)
if key in force_to_variable:
continue
if key.startswith("_"):
@ -837,9 +657,10 @@ class ParserVariable:
if comment:
obj["description"] = comment
if extra_attrs:
raise Exception(
raise DictConsistencyError(
f'"{path}" is not a valid variable, there are additional '
f'attributes: "{", ".join(extra_attrs)}"'
f'attributes: "{", ".join(extra_attrs)}"',
65, [filename]
)
self.parse_parameters(
path,
@ -849,13 +670,18 @@ class ParserVariable:
family_is_leadership is True and first_variable is False,
version,
)
self.parse_params(path, obj)
self.parse_params(path, obj, filename)
self.parse_secret_manager(path, obj, filename, version, family_is_dynamic)
exists = obj.pop("exists", None)
if path in self.paths:
if "exists" in obj and not obj.pop("exists"):
return
if not obj.pop("redefine", False):
msg = f'Variable "{path}" already exists'
raise DictConsistencyError(msg, 45, [filename])
if not self.load_unexist_redefine:
if exists is False:
return
if not obj.pop("redefine", False):
msg = _('variable "{0}" define multiple time').format(path)
raise DictConsistencyError(
msg, 45, self.paths[path].xmlfiles + [filename]
)
self.paths.add(
path,
self.paths[path].model_copy(update=obj),
@ -865,12 +691,13 @@ class ParserVariable:
)
self.paths[path].xmlfiles.append(filename)
else:
if "exists" in obj and obj.pop("exists"):
if not self.load_unexist_redefine and exists is True:
# this variable must exist
# but it's not the case
# so do nothing
return
if "redefine" in obj and obj["redefine"]:
redefine = obj.pop("redefine", False)
if not self.load_unexist_redefine and redefine:
msg = f'cannot redefine the inexisting variable "{path}"'
raise DictConsistencyError(msg, 46, [filename])
obj["path"] = path
@ -951,12 +778,16 @@ class ParserVariable:
f"at index {idx}: {err}"
) from err
def parse_params(self, path, obj):
def parse_params(self, path, obj, filename):
"""Parse variable params"""
if "params" not in obj:
return
if not isinstance(obj["params"], dict):
raise Exception(f"params must be a dict for {path}")
raise DictConsistencyError(
_("params must be a dict for {0}").format(path),
55,
[filename],
)
params = []
for key, val in obj["params"].items():
try:
@ -969,15 +800,45 @@ class ParserVariable:
is_follower=None,
attribute=None,
family_is_dynamic=None,
xmlfiles=None,
namespace=self.namespace,
xmlfiles=[filename],
)
)
except ValidationError as err:
raise Exception(
f'"{key}" has an invalid "params" for {path}: {err}'
raise DictConsistencyError(
_('"{0}" has an invalid "params" for {1}: {2}').format(
key, path, err
),
54,
[filename],
) from err
obj["params"] = params
def parse_secret_manager(self, path, obj, filename, version, family_is_dynamic):
"""Parse variable secret_manager"""
if "secret_manager" not in obj:
return
if not isinstance(obj["secret_manager"], dict):
raise DictConsistencyError(
_("secret_manager must be a dict for {0}").format(path),
64,
[filename],
)
secret_manager = {"type": "jinja",
"jinja": self.secret_pattern,
"params": obj["secret_manager"],
}
self.set_calculation(
obj,
"secret_manager",
secret_manager,
path,
family_is_dynamic,
False,
version,
[filename],
)
def add_variable(
self,
name: str,
@ -993,7 +854,6 @@ class ParserVariable:
variable["namespace"] = self.namespace
variable["version"] = version
variable["path_prefix"] = self.path_prefix
variable["xmlfiles"] = filename
variable_type = self.get_family_or_variable_type(variable)
obj = {
@ -1066,10 +926,16 @@ class ParserVariable:
calculations = calculations[1]
if not isinstance(value, dict) or attribute not in calculations:
return False
return self.check_auto_type(value)
def check_auto_type(self, value):
if "type" in value:
return value["type"] in CALCULATION_TYPES
# auto set type
typ = set(CALCULATION_TYPES) & set(value)
# XXX variable is also set to information
if typ == {"variable", "information"}:
typ = {"information"}
if len(typ) == 1:
value["type"] = list(typ)[0]
return True
@ -1094,7 +960,6 @@ class ParserVariable:
typ = calculation_object.pop("type")
calculation_object["attribute_name"] = attribute
calculation_object["path_prefix"] = self.path_prefix
calculation_object["path"] = path
calculation_object["inside_list"] = inside_list
calculation_object["version"] = version
@ -1109,6 +974,9 @@ class ParserVariable:
if isinstance(val, dict) and "type" not in val:
# auto set type
param_typ = set(CALCULATION_TYPES) & set(val)
# XXX variable is also set to information
if param_typ == {"variable", "information"}:
param_typ = {"information"}
if len(param_typ) == 1:
val["type"] = list(param_typ)[0]
if not isinstance(val, dict) or "type" not in val:
@ -1126,12 +994,19 @@ class ParserVariable:
val["family_is_dynamic"] = family_is_dynamic
val["is_follower"] = is_follower
val["attribute"] = attribute
val["namespace"] = self.namespace
val["xmlfiles"] = xmlfiles
if param_typ not in PARAM_TYPES:
raise DictConsistencyError(
f'unknown type "{param_typ}" for "{path}"',
52,
xmlfiles,
)
try:
params.append(PARAM_TYPES[param_typ](**val))
except ValidationError as err:
raise DictConsistencyError(
f'"{attribute}" has an invalid "{key}" for {path}: {err}',
f'"{attribute}" has an invalid "{key}" for "{path}": {err}',
29,
xmlfiles,
) from err
@ -1145,7 +1020,7 @@ class ParserVariable:
)
#
if typ == "identifier" and not family_is_dynamic:
msg = f'identifier calculation for "{attribute}" in "{path}" cannot be set none dynamic family'
msg = f'identifier calculation for "{attribute}" in "{path}" cannot be set variable is not in dynamic family'
raise DictConsistencyError(msg, 53, xmlfiles)
if attribute in PROPERTY_ATTRIBUTE:
calc = CALCULATION_PROPERTY_TYPES[typ](**calculation_object)
@ -1164,7 +1039,7 @@ class RougailConvert(ParserVariable):
def __init__(self, rougailconfig) -> None:
self.annotator = False
self.yaml = YAML()
self.has_namespace = False
super().__init__(rougailconfig)
def search_calculation(
@ -1194,81 +1069,20 @@ class RougailConvert(ParserVariable):
inside_list.append(key)
return inside_list, outside_list
def parse_directories(
self,
path_prefix: Optional[str] = None,
def create_namespace(
self, namespace_description: str, namespace_path: Optional[str] = None
) -> None:
"""Parse directories content"""
self._init()
if path_prefix:
n_path_prefix = normalize_family(path_prefix)
if n_path_prefix in self.parents:
raise Exception("pfffff")
root_parent = n_path_prefix
self.path_prefix = n_path_prefix
self.namespace = None
self.add_family(
n_path_prefix,
n_path_prefix,
{"description": path_prefix},
"",
False,
None,
"",
)
else:
root_parent = "."
if self.main_namespace:
directory_dict = chain(
(
(
self.main_namespace,
self.main_dictionaries,
),
),
self.extra_dictionaries.items(),
)
for namespace, extra_dirs in directory_dict:
if namespace is None:
self.namespace = namespace
else:
self.namespace = normalize_family(namespace)
if root_parent == ".":
namespace_path = self.namespace
else:
namespace_path = f"{root_parent}.{self.namespace}"
if namespace_path in self.parents:
raise Exception("pfff")
for idx, filename in enumerate(self.get_sorted_filename(extra_dirs)):
if not idx:
self.parse_family(
"",
self.namespace,
namespace_path,
{
"description": namespace,
},
"",
)
self.parse_variable_file(
filename,
namespace_path,
)
else:
self.namespace = None
if root_parent == ".":
namespace_path = ""
else:
namespace_path = f"{root_parent}"
if namespace_path in self.parents:
raise Exception("pfff")
for filename in self.get_sorted_filename(self.main_dictionaries):
self.parse_variable_file(
filename,
namespace_path,
)
if path_prefix:
self.path_prefix = None
if namespace_path is None:
namespace_path = self.namespace
self.parse_family(
"",
self.namespace,
namespace_path,
{
"description": namespace_description,
},
"",
)
def get_comment(
self,
@ -1283,27 +1097,6 @@ class RougailConvert(ParserVariable):
comment = comment.value[1:].strip()
return comment
def parse_variable_file(
self,
filename: str,
path: str,
) -> None:
"""Parse file"""
with open(filename, encoding="utf8") as file_fh:
objects = self.yaml.load(file_fh)
version = self.validate_file_version(
objects,
filename,
)
if objects is None:
return
self.parse_root_file(
filename,
path,
version,
objects,
)
def parse_root_file(
self,
filename: str,
@ -1322,38 +1115,6 @@ class RougailConvert(ParserVariable):
comment,
)
def get_sorted_filename(
self,
directories: Union[str, List[str]],
) -> Iterator[str]:
"""Sort filename"""
if not isinstance(directories, list):
directories = [directories]
if self.sort_dictionaries_all:
filenames = {}
for directory_name in directories:
directory = Path(directory_name)
if not directory.is_dir():
continue
if not self.sort_dictionaries_all:
filenames = {}
for file_path in directory.iterdir():
if file_path.suffix not in [".yml", ".yaml"]:
continue
if file_path.name in filenames:
raise DictConsistencyError(
_("duplicate dictionary file name {0}").format(file_path.name),
78,
[filenames[file_path.name][1]],
)
filenames[file_path.name] = str(file_path)
if not self.sort_dictionaries_all:
for filename in sorted(filenames):
yield filenames[filename]
if self.sort_dictionaries_all:
for filename in sorted(filenames):
yield filenames[filename]
def validate_file_version(
self,
obj: dict,
@ -1386,6 +1147,7 @@ class RougailConvert(ParserVariable):
):
"""Apply annotation"""
if not self.paths.has_value():
self.init()
self.parse_directories()
if self.annotator:
raise DictConsistencyError(
@ -1394,6 +1156,15 @@ class RougailConvert(ParserVariable):
SpaceAnnotator(self)
self.annotator = True
def parse_directories(self) -> None:
if not self.walker:
msg = _(
'invalid "structural" definition ({0}), we cannot load any structural file!'
).format(self.structurals)
raise DictConsistencyError(msg, 51, None)
self.init()
self.walker(self)
def reflect(self) -> None:
"""Apply TiramisuReflector"""
functions_files = [
@ -1406,13 +1177,13 @@ class RougailConvert(ParserVariable):
def save(
self,
filename: str,
):
"""Return tiramisu object declaration as a string"""
self._init()
self.init()
self.annotate()
self.reflect()
output = self.reflector.get_text() + "\n"
filename = self.tiramisu_cache
if filename:
with open(filename, "w", encoding="utf-8") as tiramisu:
tiramisu.write(output)

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -69,10 +69,6 @@ class DictConsistencyError(Exception):
self.errno = errno
class UpgradeError(Exception):
"""Error during XML upgrade"""
## ---- generic exceptions ----
@ -81,6 +77,10 @@ class NotFoundError(Exception):
pass
class ExtentionError(Exception):
pass
## ---- specific exceptions ----

View file

@ -8,7 +8,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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

View file

@ -1,7 +1,7 @@
"""Rougail object model
Silique (https://www.silique.fr)
Copyright (C) 2023-2024
Copyright (C) 2023-2025
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
@ -27,7 +27,10 @@ from pydantic import (
ConfigDict,
)
from tiramisu import undefined
from .utils import get_jinja_variable_to_param, get_realpath
import tiramisu
from tiramisu.config import get_common_path
from .utils import get_jinja_variable_to_param, calc_multi_for_type_variable
from .i18n import _
from .error import DictConsistencyError, VariableCalculationDependencyError
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
@ -45,7 +48,7 @@ def convert_boolean(value: str) -> bool:
return False
elif value in ["", None]:
return None
raise Exception(f'unknown boolean value "{value}"')
raise Exception(_('unknown boolean value "{0}"').format(value))
CONVERT_OPTION = {
@ -105,8 +108,38 @@ CONVERT_OPTION = {
}
def get_convert_option_types():
for typ, datas in CONVERT_OPTION.items():
obj = getattr(tiramisu, datas["opttype"])
initkwargs = datas.get("initkwargs", {})
if obj == tiramisu.SymLinkOption:
continue
if obj == tiramisu.ChoiceOption:
inst = obj("a", "a", ("a",), **initkwargs)
else:
inst = obj("a", "a", **initkwargs)
extra = getattr(inst, "_extra", {})
if not extra:
continue
params = []
for key, value in extra.items():
if key.startswith("_"):
continue
multi = False
if isinstance(value, bool):
key_type = "boolean"
elif isinstance(value, str):
key_type = "string"
elif isinstance(value, list):
key_type = "string"
multi = True
params.append((key, key_type, multi, value))
yield typ, params
class Param(BaseModel):
key: str
namespace: Optional[str]
model_config = ConfigDict(extra="forbid")
def __init__(
@ -120,6 +153,9 @@ class Param(BaseModel):
) -> None:
super().__init__(**kwargs)
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
return self.model_dump()
class AnyParam(Param):
type: str
@ -133,6 +169,31 @@ class VariableParam(Param):
whole: bool = False
optional: bool = False
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
param = super().to_param(attribute_name, objectspace, path, version, namespace, xmlfiles)
variable, identifier = objectspace.paths.get_with_dynamic(
param["variable"],
path,
version,
namespace,
xmlfiles,
)
if not variable:
if not param.get("optional"):
msg = _('cannot find variable "{0}" defined in attribute "{1}" for "{2}"').format(param["variable"], attribute_name, path)
raise DictConsistencyError(msg, 22, xmlfiles)
return None
if isinstance(variable, objectspace.family):
msg = _('the variable "{0}" is in fact a family in attribute "{1}" for "{2}"').format(variable["name"], attribute_name, path)
raise DictConsistencyError(msg, 42, xmlfiles)
if not isinstance(variable, objectspace.variable):
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(variable, attribute_name, path)
raise DictConsistencyError(msg, 44, xmlfiles)
param["variable"] = variable
if identifier:
param["identifier"] = identifier
return param
class IdentifierParam(Param):
type: str
@ -143,7 +204,7 @@ class IdentifierParam(Param):
**kwargs,
) -> None:
if not kwargs["family_is_dynamic"]:
msg = f'identifier parameter for "{kwargs["attribute"]}" in "{kwargs["path"]}" cannot be set none dynamic family'
msg = _('identifier parameter for "{0}" in "{1}" cannot be set none dynamic family').format(kwargs["attribute"], kwargs["path"])
raise DictConsistencyError(msg, 10, kwargs["xmlfiles"])
super().__init__(**kwargs)
@ -153,6 +214,27 @@ class InformationParam(Param):
information: str
variable: Optional[str] = None
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
param = super().to_param(attribute_name, objectspace, path, version, namespace, xmlfiles)
if not param["variable"]:
del param["variable"]
return param
variable, identifier = objectspace.paths.get_with_dynamic(
param["variable"],
path,
version,
namespace,
xmlfiles,
)
if not variable:
msg = _('cannot find variable "{0}" defined in "{1}" for "{2}"').format(param["variable"], attribute_name, path)
raise DictConsistencyError(msg, 14, xmlfiles)
if identifier:
msg = _('variable "{0}" defined in "{1}" for "{2}" is a dynamic variable').format(param["variable"], attribute_name, path)
raise DictConsistencyError(msg, 15, xmlfiles)
param["variable"] = variable
return param
class IndexParam(Param):
type: str
@ -161,24 +243,36 @@ class IndexParam(Param):
self,
**kwargs,
) -> None:
if not kwargs["is_follower"]:
msg = f'the variable "{kwargs["path"]}" is not a follower, so cannot have index type for param in "{kwargs["attribute"]}"'
msg = _('the variable "{0}" is not a follower, so cannot have index type for param in "{1}"').format(kwargs["path"], kwargs["attribute"])
raise DictConsistencyError(msg, 25, kwargs["xmlfiles"])
super().__init__(**kwargs)
class NamespaceParam(Param):
type: str
namespace: str
def to_param(self, attribute_name, objectspace, path, version, namespace, xmlfiles) -> dict:
namespace = self.namespace
if namespace:
namespace = objectspace.paths[namespace].description
return {'type': 'any',
'value': namespace,
'key': self.key,
}
PARAM_TYPES = {
"any": AnyParam,
"variable": VariableParam,
"identifier": IdentifierParam,
"information": InformationParam,
"index": IndexParam,
"namespace": NamespaceParam,
}
class Calculation(BaseModel):
path_prefix: Optional[str]
path: str
inside_list: bool
version: str
@ -189,64 +283,18 @@ class Calculation(BaseModel):
model_config = ConfigDict(extra="forbid")
def get_realpath(
self,
path: str,
) -> str:
return get_realpath(path, self.path_prefix)
def get_params(self, objectspace):
if not self.params:
return {}
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
params = {}
for param_obj in self.params:
param = param_obj.model_dump()
if param.get("type") == "variable":
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, identifier = objectspace.paths.get_with_dynamic(
param["variable"],
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if not variable:
if not param.get("optional"):
msg = f'cannot find variable "{param["variable"]}" defined attribute in "{self.attribute_name}" for "{self.path}"'
raise DictConsistencyError(msg, 22, self.xmlfiles)
continue
if not isinstance(variable, objectspace.variable):
raise Exception("pfff it's a family")
param["variable"] = variable
if identifier:
param["identifier"] = identifier
if param.get("type") == "information":
if param["variable"]:
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, identifier = objectspace.paths.get_with_dynamic(
param["variable"],
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if not variable:
msg = f'cannot find variable "{param["variable"]}" defined in "{self.attribute_name}" for "{self.path}"'
raise DictConsistencyError(msg, 14, self.xmlfiles)
param["variable"] = variable
if identifier:
msg = f'variable "{param["variable"]}" defined in "{self.attribute_name}" for "{self.path}" is a dynamic variable'
raise DictConsistencyError(msg, 15, self.xmlfiles)
else:
del param["variable"]
param = param_obj.to_param(self.attribute_name, objectspace, path, self.version, self.namespace, self.xmlfiles)
if param is None:
continue
params[param.pop("key")] = param
return params
@ -262,6 +310,7 @@ class JinjaCalculation(Calculation):
"validators",
"choices",
"dynamic",
"secret_manager",
]
jinja: StrictStr
params: Optional[List[Param]] = None
@ -314,7 +363,6 @@ class JinjaCalculation(Calculation):
objectspace,
variable.xmlfiles,
objectspace.functions,
self.path_prefix,
self.version,
self.namespace,
):
@ -332,6 +380,7 @@ class JinjaCalculation(Calculation):
}
if self.version != "1.0":
default["params"][true_path]["propertyerror"] = False
default["params"][true_path]["optional"] = True
if identifier:
default["params"][true_path]["identifier"] = identifier
return default
@ -340,7 +389,7 @@ class JinjaCalculation(Calculation):
self,
objectspace,
) -> dict:
if self.attribute_name == "default":
if self.attribute_name in ["default", "secret_manager"]:
if self.return_type:
raise Exception("return_type not allowed!")
variable = objectspace.paths[self.path]
@ -410,86 +459,117 @@ class _VariableCalculation(Calculation):
path = self.path
else:
path = self.ori_path
if self.version != "1.0" and objectspace.paths.regexp_relative.search(self.variable):
variable_full_path = objectspace.paths.get_full_path(
self.variable,
path,
)
elif self.version == "1.0" and "{{ suffix }}" in self.variable:
variable_full_path = self.variable.replace("{{ suffix }}", "{{ identifier }}")
else:
variable_full_path = self.variable
variable, identifier = objectspace.paths.get_with_dynamic(
self.variable,
self.path_prefix,
variable_full_path,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if variable and not isinstance(variable, objectspace.variable):
# FIXME remove the pfff
raise Exception("pfff it's a family")
return variable, identifier
if isinstance(variable, objectspace.family):
msg = _('a variable "{0}" is needs in attribute "{1}" for "{2}" but it\'s a family').format(variable_full_path, self.attribute_name, self.path)
raise DictConsistencyError(msg, 47, self.xmlfiles)
else:
msg = _('unknown object "{0}" in attribute "{1}" for "{2}"').format(variable, self.attribute_name, self.path)
raise DictConsistencyError(msg, 48, self.xmlfiles)
return variable_full_path, variable, identifier
def get_params(
self,
objectspace,
variable: "Variable",
identifier: Optional[str],
*,
needs_multi: Optional[bool] = None,
variable_in_calculation_path: str,
variable_in_calculation: "Variable",
variable_in_calculation_identifier: Optional[str],
):
if not variable:
msg = f'Variable not found "{self.variable}" for attribut "{self.attribute_name}" for variable "{self.path}"'
raise DictConsistencyError(msg, 88, self.xmlfiles)
if not variable_in_calculation:
if not objectspace.force_optional:
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
msg = _('variable "{0}" has an attribute "{1}" calculated with the unknown variable "{2}"').format(path, self.attribute_name, self.variable)
raise DictConsistencyError(msg, 88, self.xmlfiles)
return {None: [["example"]]}
param = {
"type": "variable",
"variable": variable,
"variable": variable_in_calculation,
"propertyerror": self.propertyerror,
}
if identifier:
param["identifier"] = identifier
if isinstance(self, VariableCalculation) and self.optional:
param["optional"] = self.optional
if variable_in_calculation_identifier:
param["identifier"] = variable_in_calculation_identifier
params = {None: [param]}
if self.default_values:
params["__default_value"] = self.default_values
if self.allow_none:
params["allow_none"] = True
if needs_multi is None:
if self.attribute_name != "default":
needs_multi = True
else:
needs_multi = self.path in objectspace.multis
calc_variable_is_multi = variable.path in objectspace.multis
if not calc_variable_is_multi:
if variable.path in objectspace.paths._dynamics and (
identifier is None or identifier[-1] is None
):
self_dyn_path = objectspace.paths._dynamics.get(self.path)
if self_dyn_path is not None:
var_dyn_path = objectspace.paths._dynamics[variable.path]
if self_dyn_path != var_dyn_path and not self_dyn_path.startswith(
f"{var_dyn_path}."
):
calc_variable_is_multi = True
else:
calc_variable_is_multi = True
elif identifier and "{{ identifier }}" in identifier:
calc_variable_is_multi = True
if needs_multi:
if calc_variable_is_multi:
if self.inside_list:
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is multi but is inside a list'
raise DictConsistencyError(msg, 18, self.xmlfiles)
elif not self.inside_list:
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is not multi but is not inside a list'
raise DictConsistencyError(msg, 20, self.xmlfiles)
elif self.inside_list:
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", it\'s a list'
raise DictConsistencyError(msg, 23, self.xmlfiles)
elif calc_variable_is_multi:
if (
variable.multi
or variable.path.rsplit(".", 1)[0] != self.path.rsplit(".", 1)[0]
):
# it's not a follower or not in same leadership
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is a multi'
raise DictConsistencyError(msg, 21, self.xmlfiles)
else:
params[None][0]["index"] = {"index": {"type": "index"}}
self.check_multi(objectspace, variable_in_calculation_path, variable_in_calculation)
if self.path in objectspace.followers:
multi = objectspace.multis[self.path] == "submulti"
else:
multi = self.path in objectspace.multis
if multi and not self.inside_list:
params["__internal_multi"] = True
return params
def check_multi(self, objectspace, variable_in_calculation_path, variable_in_calculation):
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
local_variable = objectspace.paths[path]
local_variable_multi, variable_in_calculation_multi = calc_multi_for_type_variable(local_variable, variable_in_calculation_path, variable_in_calculation, objectspace)
if self.attribute_name == "default":
if variable_in_calculation_multi == 'submulti':
if objectspace.paths.is_dynamic(variable_in_calculation.path):
msg = _('the variable "{0}" has an invalid "{1}" the variable "{2}" is in a sub dynamic option').format(local_variable.path, self.attribute_name, variable_in_calculation.path)
raise DictConsistencyError(msg, 69, self.xmlfiles)
else:
msg = _('the leader "{0}" has an invalid "{1}" the follower "{2}" is a multi').format(local_variable.path, self.attribute_name, variable_in_calculation.path)
raise DictConsistencyError(msg, 74, self.xmlfiles)
if not self.inside_list:
if local_variable_multi != variable_in_calculation_multi:
if local_variable_multi:
self.check_variable_in_calculation_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
self.check_variable_in_calculation_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
else:
self.check_variable_in_calculation_in_list_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
elif self.attribute_name in ["choices", "dynamic"]:
# calculated variable must be a multi
if not self.inside_list:
self.check_variable_in_calculation_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
else:
self.check_variable_in_calculation_in_list_not_multi(local_variable.path, variable_in_calculation_path, variable_in_calculation_multi)
elif variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable "{2}" must not be multi').format(local_variable.path, self.attribute_name, variable_in_calculation_path)
raise DictConsistencyError(msg, 23, self.xmlfiles)
def check_variable_in_calculation_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
if variable_in_calculation_multi is False:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable must not be a multi or the variable "{2}" must be multi').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
raise DictConsistencyError(msg, 20, self.xmlfiles)
def check_variable_in_calculation_not_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
if variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable must be a multi or the variable "{2}" must not be multi').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
raise DictConsistencyError(msg, 21, self.xmlfiles)
def check_variable_in_calculation_in_list_not_multi(self, local_variable_path, variable_in_calculation_path, variable_in_calculation_multi):
if variable_in_calculation_multi is True:
msg = _('the variable "{0}" has an invalid attribute "{1}", the variable "{2}" is multi but is inside a list').format(local_variable_path, self.attribute_name, variable_in_calculation_path)
raise DictConsistencyError(msg, 18, self.xmlfiles)
class VariableCalculation(_VariableCalculation):
attribute_name: Literal["default", "choices", "dynamic"]
@ -500,15 +580,25 @@ class VariableCalculation(_VariableCalculation):
objectspace,
) -> dict:
if self.attribute_name != "default" and self.optional:
msg = f'"{self.attribute_name}" variable shall not have an "optional" attribute for variable "{self.variable}"'
msg = _('"{0}" attribut shall not have an "optional" attribute for variable "{1}"').format(self.attribute_name, self.variable)
raise DictConsistencyError(msg, 33, self.xmlfiles)
variable, identifier = self.get_variable(objectspace)
if not variable and self.optional:
variable_in_calculation_path, variable_in_calculation, variable_in_calculation_identifier = self.get_variable(objectspace)
if (
not variable_in_calculation
and self.optional
or (objectspace.force_optional and self.attribute_name == "default")
):
raise VariableCalculationDependencyError()
if variable_in_calculation and self.attribute_name == "default":
local_variable = objectspace.paths[self.path]
if CONVERT_OPTION.get(local_variable.type, {}).get("func", str) != CONVERT_OPTION.get(variable_in_calculation.type, {}).get("func", str):
msg = _('variable "{0}" has a default value calculated with "{1}" which has incompatible type').format(self.path, self.variable)
raise DictConsistencyError(msg, 67, self.xmlfiles)
params = self.get_params(
objectspace,
variable,
identifier,
variable_in_calculation_path,
variable_in_calculation,
variable_in_calculation_identifier,
)
return {
"function": "calc_value",
@ -517,7 +607,8 @@ class VariableCalculation(_VariableCalculation):
class VariablePropertyCalculation(_VariableCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
# For python 3.9 attribute_name: Literal[*PROPERTY_ATTRIBUTE]
attribute_name: Literal["frozen", "hidden", "disabled", "mandatory"]
when: Any = undefined
when_not: Any = undefined
@ -525,41 +616,48 @@ class VariablePropertyCalculation(_VariableCalculation):
self,
objectspace,
) -> dict:
variable, identifier = self.get_variable(objectspace)
variable_in_calculation_path, variable_in_calculation, variable_in_calculation_identifier = self.get_variable(objectspace)
params = self.get_params(
objectspace,
variable,
identifier,
needs_multi=False,
variable_in_calculation_path,
variable_in_calculation,
variable_in_calculation_identifier,
)
variable = params[None][0]["variable"]
if self.when is not undefined:
if self.version == "1.0":
msg = f'when is not allowed in format version 1.0 for attribute "{self.attribute_name}" for variable "{self.path}"'
raise DictConsistencyError(msg, 103, variable.xmlfiles)
if self.when_not is not undefined:
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
raise DictConsistencyError(msg, 31, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
if self.version == "1.0":
msg = f'when_not is not allowed in format version 1.0 for attribute "{self.attribute_name}" for variable "{self.path}"'
raise DictConsistencyError(msg, 104, variable.xmlfiles)
when = self.when_not
inverse = True
if objectspace.force_optional and (not params[None] or "variable" not in params[None][0]):
params = {None: [None, None, False]}
else:
if variable.type != "boolean":
raise Exception("only boolean!")
when = True
inverse = False
variable = params[None][0]["variable"]
if self.when is not undefined:
if self.version == "1.0":
msg = _('"when" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"').format(self.attribute_name, self.path)
raise DictConsistencyError(msg, 103, variable.xmlfiles)
if self.when_not is not undefined:
msg = _('the variable "{0}" has an invalid attribute "{1}", "when" and "when_not" cannot set together').format(self.path, self.attribute_name)
raise DictConsistencyError(msg, 31, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
if self.version == "1.0":
msg = _('"when_not" is not allowed in format version 1.0 for attribute "{0}" for variable "{1}"').format(self.attribute_name, self.path)
raise DictConsistencyError(msg, 104, variable.xmlfiles)
when = self.when_not
inverse = True
else:
if variable.multi:
when = []
else:
if variable.type != "boolean":
raise Exception("only boolean!")
when = True
inverse = False
params["when"] = when
params["inverse"] = inverse
params[None].insert(0, self.attribute_name)
params["when"] = when
params["inverse"] = inverse
func = 'variable_to_property'
return {
"function": "variable_to_property",
"function": func,
"params": params,
"help": "variable_to_property",
"help": func,
}
@ -587,15 +685,20 @@ class InformationCalculation(Calculation):
path = self.ori_path
variable, identifier = objectspace.paths.get_with_dynamic(
self.variable,
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if variable is None or identifier is not None:
raise Exception("pfff")
params[None][0]["variable"] = variable
if variable is None:
if not objectspace.force_optional:
msg = _('cannot find variable "{0}" for the information "{1}" when calculating "{2}"').format(self.variable, self.information, self.attribute_name)
raise DictConsistencyError(msg, 40, variable.xmlfiles)
if identifier is not None:
msg = _('identifier not allowed for the information "{0}" when calculating "{1}"').format(self.information, self.attribute_name)
raise DictConsistencyError(msg, 41, variable.xmlfiles)
if variable:
params[None][0]["variable"] = variable
if self.default_values:
params["__default_value"] = self.default_values
return {
@ -631,7 +734,8 @@ class IdentifierCalculation(_IdentifierCalculation):
class IdentifierPropertyCalculation(_IdentifierCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
# for python 3.9 attribute_name: Literal[*PROPERTY_ATTRIBUTE]
attribute_name: Literal["frozen", "hidden", "disabled", "mandatory"]
when: Any = undefined
when_not: Any = undefined
@ -640,11 +744,11 @@ class IdentifierPropertyCalculation(_IdentifierCalculation):
objectspace,
) -> dict:
if self.version == "1.0":
msg = f'when is not allowed in format version 1.0 for attribute "{self.attribute_name}"'
msg = _('"when" is not allowed in format version 1.0 for attribute "{0}"').format(self.attribute_name)
raise DictConsistencyError(msg, 105, variable.xmlfiles)
if self.when is not undefined:
if self.when_not is not undefined:
msg = f'the identifier has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
msg = _('the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together').format(self.attribute_name)
raise DictConsistencyError(msg, 35, variable.xmlfiles)
when = self.when
inverse = False
@ -652,7 +756,7 @@ class IdentifierPropertyCalculation(_IdentifierCalculation):
when = self.when_not
inverse = True
else:
msg = f'the identifier has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
msg = _('the identifier has an invalid attribute "{0}", "when" and "when_not" cannot set together').format(self.attribute_name)
raise DictConsistencyError
params = {
None: [self.attribute_name, self.get_identifier()],
@ -674,7 +778,7 @@ class IndexCalculation(Calculation):
objectspace,
) -> dict:
if self.path not in objectspace.followers:
msg = f'the variable "{self.path}" is not a follower, so cannot have index type for "{self.attribute_name}"'
msg = _('the variable "{0}" is not a follower, so cannot have index type for "{1}"').format(self.path, self.attribute_name)
raise DictConsistencyError(msg, 60, self.xmlfiles)
return {
"function": "calc_value",
@ -682,34 +786,57 @@ class IndexCalculation(Calculation):
}
class NamespaceCalculation(Calculation):
attribute_name: Literal["default", "secret_manager"]
def to_function(
self,
objectspace,
) -> dict:
namespace = self.namespace
if namespace:
namespace = objectspace.paths[namespace].description
return {
"function": "calc_value",
"params": {None: [namespace]},
}
CALCULATION_TYPES = {
"jinja": JinjaCalculation,
"variable": VariableCalculation,
"information": InformationCalculation,
"variable": VariableCalculation,
"identifier": IdentifierCalculation,
# FOR VERSION 1.0
"suffix": IdentifierCalculation,
"index": IndexCalculation,
"namespace": NamespaceCalculation,
}
CALCULATION_PROPERTY_TYPES = {
"jinja": JinjaCalculation,
"variable": VariablePropertyCalculation,
"information": InformationCalculation,
"variable": VariablePropertyCalculation,
"identifier": IdentifierPropertyCalculation,
"index": IndexCalculation,
}
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, Calculation, None]
SECRET_BASETYPE_CALC = Union[StrictStr, JinjaCalculation]
class Family(BaseModel):
name: str
# informations
description: Optional[str] = None
type: Literal["family", "leadership", "dynamic"] = "family"
path: str
help: Optional[str] = None
mode: Optional[str] = None
# validation
type: Literal["family", "leadership", "dynamic"] = "family"
# properties
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
# others
namespace: Optional[str]
path: str
version: str
xmlfiles: List[str] = []
@ -723,30 +850,35 @@ class Dynamic(Family):
class Variable(BaseModel):
# type will be set dynamically in `annotator/value.py`, default is None
type: str = None
name: str
# user informations
description: Optional[str] = None
default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None
choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None
regexp: Optional[str] = None
params: Optional[List[Param]] = None
validators: Optional[List[Calculation]] = None
multi: Optional[bool] = None
unique: Optional[bool] = None
help: Optional[str] = None
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
mode: Optional[str] = None
examples: Optional[list] = None
test: Optional[list] = None
# validations
## type will be set dynamically in `annotator/value.py`, default is None
type: str = None
params: Optional[List[Param]] = None
regexp: Optional[str] = None
choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None
multi: Optional[bool] = None
validators: Optional[List[Calculation]] = None
# value
default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None
secret_manager: Optional[JinjaCalculation] = None
# properties
auto_save: bool = False
mandatory: Union[None, bool, Calculation] = None
empty: Union[None, bool, Calculation] = True
auto_save: bool = False
mode: Optional[str] = None
test: Optional[list] = None
examples: Optional[list] = None
unique: Optional[bool] = None
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
# others
path: str
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
@ -759,7 +891,6 @@ class SymLink(BaseModel):
opt: Variable
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid")

1
src/rougail/output_ansible Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-output-ansible/src/rougail/output_ansible

1
src/rougail/output_console Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-output-console/src/rougail/output_console

1
src/rougail/output_doc Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-output-doc/src/rougail/output_doc

View file

@ -0,0 +1 @@
../../../rougail-output-formatter/src/rougail/output_formatter

1
src/rougail/output_json Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-output-json/src/rougail/output_json

238
src/rougail/path.py Normal file
View file

@ -0,0 +1,238 @@
"""
Copyright (C) 2024-2025
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
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from typing import (
Any,
Dict,
List,
Union,
)
import logging
from re import compile, findall
from .i18n import _
from .object_model import Family, Variable
from .utils import normalize_family
class Paths:
regexp_relative = compile(r"^_*\.(.*)$")
def __init__(
self,
default_namespace: str,
) -> None:
self._data: Dict[str, Union[Variable, Family]] = {}
self._dynamics: Dict[str:str] = {}
if default_namespace is not None:
default_namespace = normalize_family(default_namespace)
self.default_namespace = default_namespace
def has_value(self) -> bool:
return self._data != {}
def add(
self,
path: str,
data: Any,
is_dynamic: bool,
dynamic: str,
*,
force: bool = False,
) -> None:
self._data[path] = data
if not force and is_dynamic:
self._dynamics[path] = dynamic
def get_full_path(
self,
path: str,
current_path: str,
):
if "{{ suffix }}" in path:
# version 1.0 and 1.1
path = path.replace("{{ suffix }}", "{{ identifier }}")
relative, subpath = path.split(".", 1)
relative_len = len(relative)
path_len = current_path.count(".")
if path_len + 1 == relative_len:
return subpath
parent_path = current_path.rsplit(".", relative_len)[0]
return parent_path + "." + subpath
def get_with_dynamic(
self,
path: str,
current_path: str,
version: str,
namespace: str,
xmlfiles: List[str],
) -> Any:
identifier = None
if version != "1.0" and self.regexp_relative.search(path):
path = self.get_full_path(
path,
current_path,
)
# elif identifier_path:
# path = f"{identifier_path}.{path}"
dynamic = None
# version 1.0
if version == "1.0":
if not path in self._data and "{{ suffix }}" not in path:
new_path = None
current_path = None
identifiers = []
for name in path.split("."):
parent_path = current_path
if current_path:
current_path += "." + name
else:
current_path = name
if current_path in self._data:
if new_path:
new_path += "." + name
else:
new_path = name
continue
for dynamic_path in self._dynamics:
if "." in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
parent_dynamic == parent_path
and name_dynamic.endswith("{{ identifier }}")
and name == name_dynamic.replace("{{ identifier }}", "")
):
new_path += "." + name_dynamic
break
regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
continue
if finded[0] == "{{ identifier }}":
identifiers.append(None)
else:
identifiers.append(finded[0])
if new_path is None:
new_path = name_dynamic
else:
new_path += "." + name_dynamic
parent_path = dynamic_path
break
else:
if new_path:
new_path += "." + name
else:
new_path = name
path = new_path
else:
identifiers = None
elif not path in self._data:
new_path = parent_path = current_path = None
identifiers = []
for name in path.split("."):
if current_path:
current_path += "." + name
else:
current_path = name
# parent_path, name_path = path.rsplit('.', 1)
if current_path in self._data:
if new_path:
new_path += "." + name
else:
new_path = name
parent_path = current_path
continue
for dynamic_path in self._dynamics:
if "." in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
"{{ identifier }}" not in name_dynamic
or parent_path != parent_dynamic
):
continue
regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
continue
if finded[0] == "{{ identifier }}":
identifiers.append(None)
else:
identifiers.append(finded[0])
if new_path is None:
new_path = name_dynamic
else:
new_path += "." + name_dynamic
parent_path = dynamic_path
break
else:
if new_path:
new_path += "." + name
else:
new_path = name
if "{{ identifier }}" in name:
identifiers.append(None)
parent_path = current_path
path = new_path
else:
identifiers = None
if path not in self._data:
return None, None
option = self._data[path]
option_namespace = option.namespace
if (
self.default_namespace not in [namespace, option_namespace]
and namespace != option_namespace
):
msg = _(
'A variable or a family located in the "{0}" namespace shall not be used in the "{1}" namespace'
).format(option_namespace, namespace)
raise DictConsistencyError(msg, 38, xmlfiles)
return option, identifiers
def __getitem__(
self,
path: str,
) -> Union[Family, Variable]:
if not path in self._data:
raise AttributeError(f"cannot find variable or family {path}")
return self._data[path]
def __contains__(
self,
path: str,
) -> bool:
return path in self._data
def __delitem__(
self,
path: str,
) -> None:
logging.info("remove empty family %s", path)
del self._data[path]
def is_dynamic(self, path: str) -> bool:
return path in self._dynamics
def get(self):
return self._data.values()

View file

@ -0,0 +1 @@
../../../rougail-structural-bitwarden/src/rougail/structural_bitwarden

View file

@ -1,6 +1,6 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024
Copyright (C) 2024-2025
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
@ -16,4 +16,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from .update import RougailUpgrade
from .object_model import Variable, Family
__all__ = ("Variable", "Family")

View file

@ -1,7 +1,7 @@
"""Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr)
Copyright (C) 2024
Copyright (C) 2024-2025
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
@ -50,7 +50,7 @@ class Annotator(Walk):
self.not_for_commandline(variable)
else:
self.manage_alternative_name(variable)
self.manage_negative_description(variable)
self.manage_negative_description(variable)
def not_for_commandline(self, variable) -> None:
self.objectspace.properties.add(variable.path, "not_for_commandline", True)
@ -91,15 +91,17 @@ class Annotator(Walk):
def manage_negative_description(self, variable) -> None:
if not variable.negative_description:
if variable.type == "boolean" and not self.objectspace.add_extra_options:
raise DictConsistencyError(
_(
'negative_description is mandatory for boolean variable, but "{0}" hasn\'t'
).format(variable.path),
200,
variable.xmlfiles,
)
return
if variable.type != "boolean":
if variable.name == variable.description:
variable.negative_description = variable.name
else:
raise DictConsistencyError(
_(
'negative_description is mandatory for boolean variable, but "{0}" hasn\'t'
).format(variable.path),
200,
variable.xmlfiles,
)
elif variable.type != "boolean":
raise DictConsistencyError(
_(
'negative_description is only available for boolean variable, but "{0}" is "{1}"'
@ -107,6 +109,7 @@ class Annotator(Walk):
201,
variable.xmlfiles,
)
self.objectspace.informations.add(
variable.path, "negative_description", variable.negative_description
)
if variable.negative_description:
self.objectspace.informations.add(
variable.path, "negative_description", variable.negative_description
)

View file

@ -2,7 +2,7 @@
Config file for Rougail-structural_commandline
Silique (https://www.silique.fr)
Copyright (C) 2024
Copyright (C) 2024-2025
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
@ -27,12 +27,14 @@ def get_rougail_config(
structural_commandline:
description: Configuration rougail-structural_commandline
commandline: false
add_extra_options:
description: Add extra options to tiramisu-cmdline-parser
negative_description: Remove extra options to tiramisu-cmdline-parser
default: true
"""
return {
"name": "exporter",
"name": "cmdline",
"process": "structural",
"options": options,
"level": 20,

View file

@ -1,7 +1,7 @@
"""Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr)
Copyright (C) 2024
Copyright (C) 2024-2025
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
@ -29,6 +29,3 @@ class Variable(BaseModel):
class Family(BaseModel):
commandline: bool = True
__all__ = ("Variable", "Family")

View file

@ -0,0 +1,155 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2022-2025
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
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from typing import Union, List, Iterator, Optional
from itertools import chain
from pathlib import Path
from ruamel.yaml import YAML
from ..utils import normalize_family
from ..path import Paths
from ..error import DictConsistencyError
from ..i18n import _
class Walker:
def __init__(
self,
convert,
) -> None:
"""Parse directories content"""
self.convert = convert
self.yaml = YAML()
rougailconfig = self.convert.rougailconfig
self.sort_dictionaries_all = rougailconfig["sort_dictionaries_all"]
if rougailconfig["main_namespace"]:
self.convert.paths = Paths(rougailconfig["main_namespace"])
self.load_with_extra(
rougailconfig["extra_dictionaries"],
rougailconfig["main_namespace"],
rougailconfig["main_dictionaries"],
)
else:
self.convert.namespace = None
namespace_path = ""
if namespace_path in self.convert.parents:
raise Exception("pfff")
for filename in self.get_sorted_filename(
rougailconfig["main_dictionaries"]
):
self.parse_variable_file(
filename,
namespace_path,
)
def load_with_extra(
self,
extra_structures: dict,
main_namespace: Optional[str] = None,
main_structures: Optional[List[str]] = None,
) -> None:
self.convert.has_namespace = True
if main_namespace:
directory_dict = chain(
(
(
main_namespace,
main_structures,
),
),
extra_structures.items(),
)
else:
directory_dict = extra_structures.items()
for namespace, extra_dirs in directory_dict:
# if namespace is None:
# self.convert.namespace = namespace
# else:
self.convert.namespace = normalize_family(namespace)
namespace_path = self.convert.namespace
if namespace_path in self.convert.parents:
raise Exception("pfff")
for idx, filename in enumerate(self.get_sorted_filename(extra_dirs)):
if not idx:
# create only for the first file
self.convert.create_namespace(namespace, namespace_path)
self.parse_variable_file(
filename,
namespace_path,
)
def get_sorted_filename(
self,
directories: Union[str, List[str]],
) -> Iterator[str]:
"""Sort filename"""
if not isinstance(directories, list):
directories = [directories]
if self.sort_dictionaries_all:
filenames = {}
for directory_name in directories:
directory = Path(directory_name)
if not self.sort_dictionaries_all:
filenames = {}
if directory.is_file():
self.get_filename(directory, filenames)
else:
for file_path in directory.iterdir():
self.get_filename(file_path, filenames)
if not self.sort_dictionaries_all:
for filename in sorted(filenames):
yield filenames[filename]
if self.sort_dictionaries_all:
for filename in sorted(filenames):
yield filenames[filename]
def get_filename(self, file_path, filenames: List[str]) -> None:
if file_path.suffix not in [".yml", ".yaml"]:
return
if file_path.name in filenames:
raise DictConsistencyError(
_("duplicate dictionary file name {0}").format(file_path.name),
78,
[filenames[file_path.name], str(file_path)],
)
filenames[file_path.name] = str(file_path)
def parse_variable_file(
self,
filename: str,
path: str,
) -> None:
"""Parse file"""
with open(filename, encoding="utf8") as file_fh:
objects = self.yaml.load(file_fh)
version = self.convert.validate_file_version(
objects,
filename,
)
if objects is None:
return
self.convert.parse_root_file(
filename,
path,
version,
objects,
)
__all__ = ("Walker",)

View file

@ -0,0 +1,103 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2024-2025
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
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from ..utils import _
def get_rougail_config(
*,
backward_compatibility=True,
) -> dict:
if backward_compatibility:
main_namespace_default = "rougail"
else:
main_namespace_default = "null"
options = f"""
main_dictionaries:
description: {_("Directories where dictionary files are placed")}
type: unix_filename
alternative_name: m
params:
allow_relative: True
test_existence: True
multi: true
disabled:
jinja: >-
{{% if 'directory' not in step.structural %}}
directory is not in step.structural
{{% endif %}}
{{% if cli is defined and cli.versions is defined and cli.versions %}}
cli.versions is specified
{{% endif %}}
sort_dictionaries_all:
description: {_("Sort dictionaries from differents directories")}
negative_description: Sort dictionaries directory by directory
default: false
disabled:
jinja: >-
{{% if 'directory' not in step.structural %}}
directory is not in step.structural
{{% endif %}}
main_namespace:
description: {_("Main namespace name")}
default: {main_namespace_default}
alternative_name: s
mandatory: false
disabled:
jinja: >-
{{% if 'directory' not in step.structural %}}
directory is not in step.structural
{{% endif %}}
extra_dictionaries:
description: {_("Extra namespaces")}
type: leadership
disabled:
jinja: >-
{{% if 'directory' not in step.structural %}}
directory is not in step.structural
{{% endif %}}
names:
description: {_("Extra namespace name")}
alternative_name: xn
multi: true
mandatory: false
directories:
description: {_("Directories where extra dictionary files are placed")}
alternative_name: xd
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- directory
multi: true
"""
return {
"name": "directory",
"process": "structural",
"options": options,
"level": 5,
}
__all__ = "get_rougail_config"

View file

@ -0,0 +1 @@
../../../risotto/src/rougail/structural_risotto

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -27,10 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
try:
from tiramisu5 import DynOptionDescription, calc_value
except ModuleNotFoundError:
from tiramisu import DynOptionDescription, calc_value
from importlib.machinery import SourceFileLoader as _SourceFileLoader
from importlib.util import (
spec_from_loader as _spec_from_loader,
@ -40,9 +36,10 @@ from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment
from rougail.object_model import CONVERT_OPTION
from rougail.error import display_xmlfiles
from tiramisu import function_waiting_for_error
from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError
from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError, CancelParam
from .utils import normalize_family
from .i18n import _
global func
@ -90,8 +87,10 @@ def test_propertyerror(value: Any) -> bool:
ENV.tests["propertyerror"] = test_propertyerror
def load_functions(path):
def load_functions(path, dict_func=None):
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
if dict_func is None:
dict_func = func
loader = _SourceFileLoader("func", path)
spec = _spec_from_loader(loader.name, loader)
func_ = _module_from_spec(spec)
@ -99,16 +98,32 @@ def load_functions(path):
for function in dir(func_):
if function.startswith("_"):
continue
func[function] = getattr(func_, function)
dict_func[function] = getattr(func_, function)
def rougail_calc_value(*args, __default_value=None, **kwargs):
def rougail_calc_value(*args, __default_value=None, __internal_multi=False, **kwargs):
values = calc_value(*args, **kwargs)
if values is None and __internal_multi:
values = []
if __default_value is not None and values in [None, []]:
return __default_value
return values
def kw_to_string(kw, root=None):
for name, data in kw.items():
if root is None:
path = name
else:
path = root + '.' + name
if isinstance(data, dict):
yield from kw_to_string(data, root=path)
else:
yield f"{path}={data}"
pass
@function_waiting_for_error
def jinja_to_function(
__internal_variable,
@ -128,9 +143,15 @@ def jinja_to_function(
if "." in key:
c_kw = kw
path, var = key.rsplit(".", 1)
if isinstance(value, CancelParam):
count_o_path = value.origin_path.count(".") - value.current_path.count(
"."
)
path = path.rsplit(".", count_o_path)[0]
for subkey in path.split("."):
c_kw = c_kw.setdefault(subkey, {})
c_kw[var] = value
if not isinstance(value, CancelParam):
c_kw[var] = value
else:
if key in kw:
raise ConfigError(
@ -140,12 +161,13 @@ def jinja_to_function(
try:
values = ENV.get_template(__internal_jinja).render(kw, **func).strip()
except Exception as err:
kw_str = ", ".join(kw_to_string(kw))
raise ConfigError(
f'cannot calculating "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}'
_('cannot calculating "{0}" attribute for variable "{1}" in {2} with parameters "{3}": {4}').format(__internal_attribute, __internal_variable, display_xmlfiles(__internal_files), kw_str, err)
) from err
convert = CONVERT_OPTION[__internal_type].get("func", str)
if __internal_multi:
values = [convert(val) for val in values.split("\n") if val != ""]
values = [convert(val.strip()) for val in values.split("\n") if val.strip() != ""]
if not values and __default_value is not None:
return __default_value
return values
@ -161,7 +183,7 @@ def jinja_to_function(
return values
def variable_to_property(prop, value, when, inverse):
def variable_to_property(prop, value, when, inverse, **kwargs):
if isinstance(value, PropertiesOptionError):
raise value from value
if inverse:
@ -200,6 +222,7 @@ func["jinja_to_property"] = jinja_to_property
func["jinja_to_property_help"] = jinja_to_property_help
func["variable_to_property"] = variable_to_property
func["valid_with_jinja"] = valid_with_jinja
func["normalize_family"] = normalize_family
class ConvertDynOptionDescription(DynOptionDescription):

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -87,7 +87,7 @@ class TiramisuReflector:
continue
self.text["header"].append(f"load_functions('{funcs_path}')")
if self.objectspace.export_with_import:
if objectspace.main_namespace:
if self.objectspace.has_namespace:
self.text["header"].extend(
[
"try:",
@ -295,40 +295,13 @@ class Common:
):
"""Populate variable parameters"""
if not isinstance(param, dict):
if isinstance(param, str):
value = self.convert_str(param)
else:
value = param
return f"ParamValue({value})"
param = {"type": "any",
"value": param,
}
if param["type"] == "value":
return f"ParamValue({param['value']})"
if param["type"] == "information":
# default? really?
if self.elt.multi:
default = []
else:
default = None
if "variable" in param:
if param["variable"].path == self.elt.path:
return f'ParamSelfInformation("{param["information"]}", {default})'
information_variable_path = param["variable"].path
information_variable = self.tiramisu.reflector_objects[
information_variable_path
]
if information_variable_path not in self.calls:
option_name = information_variable.get(self.calls, self.elt.path)
return f'ParamInformation("{param["information"]}", {default}, option={option_name})'
else:
information = (
f'ParamInformation("{param["information"]}", {default})'
)
information_name = self.tiramisu.get_information_name()
self.tiramisu.text["option"].append(
f"{information_name} = {information}"
)
information_variable.informations.append(information_name)
return information_name
return f'ParamInformation("{param["information"]}", {default})'
return self.build_information_param(param)
if param["type"] == "identifier":
if "identifier" in param and param["identifier"] != None:
return f"ParamIdentifier(identifier_index={param['identifier']})"
@ -336,13 +309,7 @@ class Common:
if param["type"] == "index":
return "ParamIndex()"
if param["type"] == "variable":
return self.build_option_param(
param["variable"],
param.get("propertyerror", True),
param.get("identifier"),
param.get("dynamic"),
param.get("whole", False),
)
return self.build_variable_param(param)
if param["type"] == "any":
if isinstance(param["value"], str):
value = self.convert_str(param["value"])
@ -351,15 +318,42 @@ class Common:
return "ParamValue(" + value + ")"
raise Exception("pfff")
def build_option_param(
def build_information_param(self, param: dict) -> str:
# default? really?
if self.elt.multi:
default = []
else:
default = None
if "variable" in param:
information_variable_path = param["variable"].path
if information_variable_path == self.elt.path:
return f'ParamSelfInformation("{param["information"]}", {default})'
information_variable = self.tiramisu.reflector_objects[
information_variable_path
]
if information_variable_path not in self.calls:
option_name = information_variable.get(self.calls, self.elt.path)
return f'ParamInformation("{param["information"]}", {default}, option={option_name})'
else:
# if we want to get information from the a parent family
information = (
f'ParamInformation("{param["information"]}", {default})'
)
information_name = self.tiramisu.get_information_name()
self.tiramisu.text["option"].append(
f"{information_name} = {information}"
)
information_variable.informations.append(information_name)
return information_name
return f'ParamInformation("{param["information"]}", {default})'
def build_variable_param(
self,
variable,
propertyerror,
identifier: Optional[str],
dynamic,
whole: bool,
param: dict,
) -> str:
"""build variable parameters"""
variable = param["variable"]
whole = param.get("whole", False)
if variable.path == self.elt.path:
return f"ParamSelfOption(whole={whole})"
if whole:
@ -369,7 +363,13 @@ class Common:
self.calls, self.elt.path
)
params = [f"{option_name}"]
identifier = param.get("identifier")
if identifier is not None:
if not self.objectspace.paths.is_dynamic(variable.path):
msg = _("internal error, {0} is not a dynamic variable").format(
variable.path
)
raise DictConsistencyError(msg, 49, self.elt.xmlfiles)
param_type = "ParamDynOption"
identifiers = []
for ident in identifier:
@ -377,9 +377,11 @@ class Common:
ident = self.convert_str(ident)
identifiers.append(str(ident))
params.append("[" + ", ".join(identifiers) + "]")
if param.get("optional", False):
params.append("optional=True")
else:
param_type = "ParamOption"
if not propertyerror:
if not param.get("propertyerror", True):
params.append("notraisepropertyerror=True")
return f'{param_type}({", ".join(params)})'
@ -393,7 +395,7 @@ class Common:
kwargs = []
if "params" in child:
for key, value in child["params"].items():
if not key:
if key is None:
for val in value:
new_args.append(self.populate_param(val))
else:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
../../../rougail-user-data-ansible/src/rougail/user_data_ansible

View file

@ -0,0 +1 @@
../../../rougail-user-data-bitwarden/src/rougail/user_data_bitwarden

View file

@ -0,0 +1 @@
../../../rougail-user-data-environment/src/rougail/user_data_environment

View file

@ -0,0 +1 @@
../../../risotto/src/rougail/user_data_risotto

1
src/rougail/user_data_yaml Symbolic link
View file

@ -0,0 +1 @@
../../../rougail-user-data-yaml/src/rougail/user_data_yaml

267
src/rougail/user_datas.py Normal file
View file

@ -0,0 +1,267 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2022-2025
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from typing import List
from re import findall
from rougail.utils import normalize_family
from tiramisu import undefined, Calculation
from tiramisu.error import (
PropertiesOptionError,
LeadershipError,
ConfigError,
CancelParam,
)
from .error import DictConsistencyError
from .i18n import _
from .object_model import CONVERT_OPTION
class UserDatas:
def __init__(self, config) -> None:
self.config = config
def user_datas(self, user_datas: List[dict]):
self.values = {}
self.errors = []
self.warnings = []
self._populate_values(user_datas)
self._auto_configure_dynamics()
self._populate_config()
self._populate_error_warnings()
return {
"errors": self.errors,
"warnings": self.warnings,
}
def _populate_values(self, user_datas):
for datas in user_datas:
options = datas.get("options", {})
source = datas["source"]
for name, data in datas.get("values", {}).items():
self.values[name] = {
"source": source,
"values": data,
"options": options.copy(),
}
self.errors.extend(datas.get("errors", []))
self.warnings.extend(datas.get("warnings", []))
def _get_variable(self, config):
for subconfig in config:
if subconfig.isoptiondescription():
yield from self._get_variable(subconfig)
else:
yield subconfig
def _auto_configure_dynamics(self):
cache = {}
added = []
for path, data in list(self.values.items()):
try:
option = self.config.option(path)
option.name()
except (ConfigError, PropertiesOptionError):
pass
except AttributeError:
self._not_found_is_dynamic(self.config, path, cache, added, data)
def _not_found_is_dynamic(self, config, path, cache, added, data):
"""if path is not found, check if parent is a dynamic family"""
current_path = ""
identifiers = []
# get parent
for name in path.split(".")[:-1]:
if current_path:
current_path += "."
current_path += name
if current_path in cache:
config, identifier = cache[current_path]
identifiers.append(identifier)
continue
tconfig = config.option(name)
try:
tconfig.group_type()
# object exists, so current config is the temporary config
config = tconfig
if config.isdynamic(only_self=True):
identifiers.append(config.identifiers()[-1])
except AttributeError:
# try to found the good dynamic family
for tconfig in config.list(uncalculated=True):
if not tconfig.isdynamic(only_self=True):
# it's not a dynamic variable
continue
identifier = self._get_identifier(tconfig.name(), name)
if identifier != normalize_family(identifier):
msg = _('cannot load variable path "{0}", the identifier "{1}" is not valid in {2}').format(path, identifier, data["source"])
self.warnings.append(msg)
continue
if identifier is None:
# it's a dynamic variable but doesn't match the current name
continue
# get the variable associate to this dynamic family
dynamic_variable = tconfig.information.get(
"dynamic_variable",
None,
)
if not dynamic_variable:
# it's the good dynamic variable but it's not linked to a variable
# so cannot change the variable
continue
option_type = self.config.option(dynamic_variable).information.get(
"type"
)
dyn_options_values = (
self.config.option(dynamic_variable).get().impl_getdefault()
)
if "{{ identifier }}" in dynamic_variable:
for s in identifiers:
dynamic_variable = dynamic_variable.replace(
"{{ identifier }}", str(s), 1
)
# do not add values in variable if has already a value
if dynamic_variable not in self.values and not dyn_options_values:
self.values[dynamic_variable] = {"values": []}
added.append(dynamic_variable)
elif dynamic_variable not in added:
continue
config = tconfig
identifiers.append(identifier)
typ = CONVERT_OPTION.get(option_type, {}).get("func")
if typ:
identifier = typ(identifier)
if identifier not in self.values[dynamic_variable]["values"]:
self.values[dynamic_variable]["values"].append(identifier)
cache[current_path] = config, identifier
break
def _populate_config(self):
while self.values:
value_is_set = False
for option in self._get_variable(self.config):
path = option.path()
if path not in self.values:
continue
options = self.values[path].get("options", {})
if (
options.get("allow_secrets_variables", True) is False
and option.type() == "password"
):
self.errors.append(
_(
'the variable "{0}" contains secrets and should not be defined in {1}'
).format(path, self.values[path]["source"])
)
continue
value = self.values[path]["values"]
needs_convert = options.get("needs_convert", False)
# converted value
if option.ismulti():
if options.get("multi_separator") and not isinstance(value, list):
value = value.split(options["multi_separator"])
self.values[path]["values"] = value
if option.issubmulti():
value = [[val] for val in value]
if needs_convert:
if option.issubmulti():
for idx, val in enumerate(value):
value[idx] = [convert_value(option, v) for v in val]
else:
value = [convert_value(option, val) for val in value]
self.values[path]["values"] = value
self.values[path]["options"]["needs_convert"] = False
elif needs_convert:
value = convert_value(option, value)
index = option.index()
if index is not None:
if not isinstance(value, list) or index >= len(value):
continue
value = value[index]
try:
option.value.set(value)
value_is_set = True
# value is correctly set, remove variable to the set
if index is not None:
# if it's a follower waiting for all followers are sets
self.values[path]["values"][index] = undefined
if set(self.values[path]["values"]) == {undefined}:
self.values.pop(path)
else:
self.values.pop(path)
except Exception:
if path != option.path():
self.values[option.path()] = self.values.pop(path)
if not value_is_set:
break
def _get_identifier(self, true_name, name) -> str:
if true_name == "{{ identifier }}":
return name
regexp = true_name.replace("{{ identifier }}", "(.*)")
finded = findall(regexp, name)
if len(finded) != 1 or not finded[0]:
return None
return finded[0]
def _populate_error_warnings(self):
# we don't find variable, apply value just to get error or warning messages
for path, data in self.values.items():
try:
option = self.config.option(path)
if option.isoptiondescription():
self.errors.warnings(
_('the variable "{0}" is a family in {1}').format(
option.path(),
data["source"],
)
)
continue
value = data["values"]
if option.isfollower():
for index, val in enumerate(value):
if val is undefined or isinstance(val, CancelParam):
continue
self.config.option(path, index).value.set(val)
else:
option.value.set(value)
except AttributeError as err:
self.warnings.append(_('{0} loaded from {1}').format(err, data["source"]))
except (ValueError, LeadershipError, PropertiesOptionError) as err:
self.warnings.append(_('{0} in {1}').format(err, data["source"]))
def convert_value(option, value):
if value == "":
return None
option_type = option.information.get("type")
if option_type == "choice":
choices = option.value.list()
if value not in choices and isinstance(value, str):
# FIXME add other tests (boolean, float, ...)
if value.isnumeric() and int(value) in choices:
value = int(value)
func = CONVERT_OPTION.get(option_type, {}).get("func")
if func:
return func(value)
return value

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
Silique (https://www.silique.fr)
Copyright (C) 2022-2024
Copyright (C) 2022-2025
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
@ -78,13 +78,10 @@ def load_modules(name, module) -> List[str]:
return eosfunc
def get_realpath(
path: str,
path_prefix: str,
) -> str:
if path_prefix:
return f"{path_prefix}.{path}"
return path
# def get_realpath(
# path: str,
# ) -> str:
# return path
def get_jinja_variable_to_param(
@ -93,7 +90,6 @@ def get_jinja_variable_to_param(
objectspace,
xmlfiles,
functions,
path_prefix,
version,
namespace,
):
@ -118,6 +114,8 @@ def get_jinja_variable_to_param(
jinja_text, current_path, err
)
raise DictConsistencyError(msg, 39, xmlfiles) from err
except AttributeError:
pass
variables = list(variables)
variables.sort(reverse=True)
founded_variables = {}
@ -125,7 +123,6 @@ def get_jinja_variable_to_param(
for variable_path in variables:
variable, identifier = objectspace.paths.get_with_dynamic(
variable_path,
path_prefix,
current_path,
version,
namespace,
@ -152,7 +149,6 @@ def get_jinja_variable_to_param(
vpath = vpath.rsplit(".", 1)[0]
variable, identifier = objectspace.paths.get_with_dynamic(
vpath,
path_prefix,
current_path,
version,
namespace,
@ -165,3 +161,41 @@ def get_jinja_variable_to_param(
yield {}, None, root_path
for variable_path, data in founded_variables.items():
yield data[1], data[0], variable_path
def calc_multi_for_type_variable(local_variable: 'Variable', variable_in_calculation_path: str, variable_in_calculation: 'Variable', objectspace: 'RougailConvert') -> Union[bool, str]:
variable_in_calculation_multi = variable_in_calculation.multi
if local_variable.path in objectspace.families:
# it's a family
local_variable_multi = None
else:
local_variable_multi = local_variable.multi
# variable is a leader
if variable_in_calculation.path in objectspace.leaders:
local_variable_parent = local_variable.path.rsplit('.', 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit('.', 1)[0]
if local_variable_parent == variable_in_calculation_parent:
variable_in_calculation_multi = False
# variable is a follower
elif variable_in_calculation.path in objectspace.followers:
local_variable_parent = local_variable.path.rsplit('.', 1)[0]
variable_in_calculation_parent = variable_in_calculation.path.rsplit('.', 1)[0]
if local_variable_parent != variable_in_calculation_parent:
if variable_in_calculation_multi:
variable_in_calculation_multi = 'submulti'
else:
variable_in_calculation_multi = True
# variable is in a dynamic family
if objectspace.paths.is_dynamic(variable_in_calculation.path):
common_path = get_common_path(local_variable.path, variable_in_calculation_path)
common_variable_path = variable_in_calculation_path
if common_path:
common_variable_path = common_variable_path[len(common_path) + 1:]
count_identifiers = common_variable_path.count('{{ identifier }}')
if count_identifiers == 1:
if variable_in_calculation_multi is False:
variable_in_calculation_multi = True
else:
variable_in_calculation_multi = 'submulti'
elif count_identifiers > 1:
variable_in_calculation_multi = 'submulti'
return local_variable_multi, variable_in_calculation_multi

View file

@ -0,0 +1,8 @@
version: 1.1
file:
type: unix_filename
params:
allow_relative: true
default:
test/unknown_file

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,15 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"}))
optiondescription_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,3 +0,0 @@
---
_version: '1.1'
version: # a variable

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,19 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_6 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_5 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_6], properties=frozenset({"basic"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,3 +0,0 @@
---
version: '1.0'
empty:

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,19 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_6 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_5 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_6], properties=frozenset({"basic"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,10 +0,0 @@
---
version: 1.1
var1: "no" # a first variable
var2:
description: a second variable
multi: true
default:
jinja: |
{{ _.var1 }}
description: the value of var1

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
@ -12,6 +12,6 @@ ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_rougail.var2'] = "{{ _.var1 }}\n"
option_2 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -1,23 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_1.rougail.var2'] = "{{ _.var1 }}\n"
dict_env['default_2.rougail.var2'] = "{{ _.var1 }}\n"
option_3 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_1.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("1.rougail.var2"), '_.var1': ParamOption(option_3, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_7 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_2.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("2.rougail.var2"), '_.var1': ParamOption(option_7, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_7, option_8], properties=frozenset({"standard"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -2,11 +2,11 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_var2'] = "{{ _.var1 }}\n"
option_1 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -1,15 +0,0 @@
---
version: 1.1
var1: # a first variable
- 'no'
- 'yes'
- maybe
var2:
description: a second variable
multi: true
default:
jinja: |
{% for val in _.var1 %}
{{ val }}
{% endfor %}
description: the value of _.var1

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
@ -12,6 +12,6 @@ ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_2 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -1,23 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_1.rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
dict_env['default_2.rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_3 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_1.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("1.rougail.var2"), '_.var1': ParamOption(option_3, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_7 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_2.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("2.rougail.var2"), '_.var1': ParamOption(option_7, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_7, option_8], properties=frozenset({"standard"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -2,11 +2,11 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_1 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1, notraisepropertyerror=True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -1,15 +0,0 @@
---
version: 1.1
var1:
description: a first variable
multi: true
type: domainname
params:
allow_ip: true
var2:
description: a second variable
default:
type: variable
variable: _.var1

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
@ -11,6 +11,6 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_3 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_2)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
option_3 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)})), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -1,21 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_4 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_3)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_7 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_8 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_7)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_7, option_8], properties=frozenset({"basic"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -2,10 +2,10 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_2 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_1)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
option_2 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -1,4 +0,0 @@
---
version: '1.0'
var1:
description: a variable

View file

@ -1,4 +0,0 @@
---
version: "1.0"
var2:
description: a variable

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,21 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_7 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_7, option_8], properties=frozenset({"basic"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,5 +0,0 @@
---
version: '1.0'
without_type:
description: a variable
default: non

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,19 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_6 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_5 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_6], properties=frozenset({"standard"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,18 +0,0 @@
---
version: '1.1'
var1: true # the first variable
var2:
description: the second variable
default: true
var3:
description: the third variable
type: boolean
default: true
var4: false # the forth variable
var5:
description: the fifth variable
default: false
var6:
description: the sixth variable
type: boolean
default: false

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,29 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = BoolOption(name="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_4 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_5 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_6 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_7 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_8 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4, option_5, option_6, option_7, option_8], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_11 = BoolOption(name="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_12 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_13 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_14 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_15 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_16 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
optiondescription_10 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_11, option_12, option_13, option_14, option_15, option_16], properties=frozenset({"standard"}))
optiondescription_9 = OptionDescription(name="2", doc="2", children=[optiondescription_10], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_9])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,6 +0,0 @@
---
version: '1.1'
variable:
description: a variable
type: boolean
mandatory: false

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,19 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = BoolOption(name="variable", doc="a variable", default=True, properties=frozenset({"standard"}), informations={'type': 'boolean'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_6 = BoolOption(name="variable", doc="a variable", default=True, properties=frozenset({"standard"}), informations={'type': 'boolean'})
optiondescription_5 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_6], properties=frozenset({"standard"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,42 +0,0 @@
---
version: '1.1'
var1:
description: the first variable
choices:
- a
- b
- c
var2:
description: the second variable
choices:
- a
- b
- c
var3:
description: the third variable
choices:
- a
- b
- c
mandatory: false
var4:
description: the forth variable
choices:
- null
- b
- c
mandatory: false
var5:
description: the fifth variable
choices:
- a
- b
- c
default: a
var6:
description: the sixth variable
choices:
- 1
- 2
- 3
default: 1

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,29 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = ChoiceOption(name="var1", doc="the first variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_4 = ChoiceOption(name="var2", doc="the second variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_5 = ChoiceOption(name="var3", doc="the third variable", values=("a", "b", "c", None), properties=frozenset({"standard"}), informations={'type': 'choice'})
option_6 = ChoiceOption(name="var4", doc="the forth variable", values=(None, "b", "c"), properties=frozenset({"standard"}), informations={'type': 'choice'})
option_7 = ChoiceOption(name="var5", doc="the fifth variable", values=("a", "b", "c"), default="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_8 = ChoiceOption(name="var6", doc="the sixth variable", values=(1, 2, 3), default=1, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4, option_5, option_6, option_7, option_8], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_11 = ChoiceOption(name="var1", doc="the first variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_12 = ChoiceOption(name="var2", doc="the second variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_13 = ChoiceOption(name="var3", doc="the third variable", values=("a", "b", "c", None), properties=frozenset({"standard"}), informations={'type': 'choice'})
option_14 = ChoiceOption(name="var4", doc="the forth variable", values=(None, "b", "c"), properties=frozenset({"standard"}), informations={'type': 'choice'})
option_15 = ChoiceOption(name="var5", doc="the fifth variable", values=("a", "b", "c"), default="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_16 = ChoiceOption(name="var6", doc="the sixth variable", values=(1, 2, 3), default=1, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_10 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_11, option_12, option_13, option_14, option_15, option_16], properties=frozenset({"basic"}))
optiondescription_9 = OptionDescription(name="2", doc="2", children=[optiondescription_10], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_9])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -1,12 +0,0 @@
---
version: 1.1
var:
description: a variable
default: 9
choices:
jinja: |
{% for n in trange(0, 10) %}
{{ n }}
{% endfor %}
return_type: 'number'
description: choices is 0 to 9

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
@ -11,6 +11,6 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['choices_rougail.var'] = "{% for n in trange(0, 10) %}\n{{ n }}\n{% endfor %}\n"
option_2 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_rougail.var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_6choice_calculation/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("rougail.var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_2 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_rougail.var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_6choice_calculation/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("rougail.var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -1,21 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['choices_1.rougail.var'] = "{% for n in trange(0, 10) %}\n{{ n }}\n{% endfor %}\n"
dict_env['choices_2.rougail.var'] = "{% for n in trange(0, 10) %}\n{{ n }}\n{% endfor %}\n"
option_3 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_1.rougail.var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_6choice_calculation/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("1.rougail.var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_6 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_2.rougail.var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_6choice_calculation/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("2.rougail.var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_5 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_6], properties=frozenset({"standard"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -2,10 +2,10 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['choices_var'] = "{% for n in trange(0, 10) %}\n{{ n }}\n{% endfor %}\n"
option_1 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_6choice_calculation/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_1 = ChoiceOption(name="var", doc="a variable", values=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("choices_var"), '__internal_type': ParamValue("number"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['../rougail-tests/structures/00_6choice_calculation/rougail/00-base.yml']), '__internal_attribute': ParamValue("choices"), '__internal_variable': ParamValue("var")})), default=9, properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": null
},
"rougail.var2": {
"owner": "default",
"value": null
}
}

View file

@ -0,0 +1,4 @@
{
"rougail.var1": null,
"rougail.var2": null
}

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": null
},
"rougail.var2": {
"owner": "default",
"value": null
}
}

View file

@ -0,0 +1 @@
["rougail.var1", "rougail.var2"]

View file

@ -0,0 +1,16 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = ChoiceOption(name="var1", doc="the first variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_3 = ChoiceOption(name="var2", doc="the second variable", values=("a", "b", "c"), default=Calculation(func['calc_value'], Params((ParamOption(option_2)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('../rougail-tests/funcs/test.py')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = ChoiceOption(name="var1", doc="the first variable", values=("a", "b", "c"), properties=frozenset({"basic", "mandatory"}), informations={'type': 'choice'})
option_2 = ChoiceOption(name="var2", doc="the second variable", values=("a", "b", "c"), default=Calculation(func['calc_value'], Params((ParamOption(option_1)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -1,11 +0,0 @@
---
version: '1.1'
var1: # a second variable
- a
- b
- c
var2:
description: a first variable
default: a
choices:
variable: _.var1

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
load_functions('../rougail-tests/funcs/test.py')
try:
groups.namespace
except:

View file

@ -1,21 +0,0 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
load_functions('tests/dictionaries/../eosfunc/test.py')
try:
groups.namespace
except:
groups.addgroup('namespace')
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced")
option_3 = StrOption(name="var1", doc="a second variable", multi=True, default=["a", "b", "c"], default_multi="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_4 = ChoiceOption(name="var2", doc="a first variable", values=Calculation(func['calc_value'], Params((ParamOption(option_3)))), default="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_3, option_4], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_7 = StrOption(name="var1", doc="a second variable", multi=True, default=["a", "b", "c"], default_multi="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_8 = ChoiceOption(name="var2", doc="a first variable", values=Calculation(func['calc_value'], Params((ParamOption(option_7)))), default="a", properties=frozenset({"mandatory", "standard"}), informations={'type': 'choice'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_7, option_8], properties=frozenset({"standard"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

Some files were not shown because too many files have changed in this diff Show more