Compare commits

..

107 commits

Author SHA1 Message Date
b120dd8e69 bump: version 1.2.0a34 → 1.2.0a35 2025-10-02 22:41:32 +02:00
64e2d137c5 feat: add warning class 2025-10-02 22:41:19 +02:00
fbba016007 bump: version 1.2.0a33 → 1.2.0a34 2025-09-30 22:02:21 +02:00
f5f55dd853 feat: for formatter 2025-09-30 22:02:07 +02:00
899226f7d2 bump: version 1.2.0a32 → 1.2.0a33 2025-09-29 21:27:36 +02:00
4bf7274871 fix: better doc for calculation with unknown variable 2025-09-29 21:19:13 +02:00
dcb1800346 bump: version 1.2.0a31 → 1.2.0a32 2025-09-29 10:58:37 +02:00
5e0af148d5 feat(#28): default value for a calculated variable with an unknown optional variable 2025-09-28 15:44:11 +02:00
bbcffffb82 feat(#26): cidr and network_cidr type is depreciate 2025-09-24 22:14:59 +02:00
99dddf20a9 feat(#25): add integer type which will replace number type 2025-09-23 20:30:16 +02:00
adff39d4d3 bump: version 1.2.0a30 → 1.2.0a31 2025-09-22 14:36:42 +02:00
bfc59e8084 fix: dictionary => structure 2025-09-22 14:36:32 +02:00
e2d5a30c21 bump: version 1.2.0a29 → 1.2.0a30 2025-09-20 18:46:20 +02:00
9ee03b22bb feat(40): better conflict error message with dynamic name 2025-09-20 18:46:08 +02:00
c8d5656094 feat: add new get_root_option function 2025-08-28 21:24:45 +02:00
c4b90cbe42 fix: update test 2025-08-28 21:24:04 +02:00
62620d9b2e bump: version 1.2.0a28 → 1.2.0a29 2025-06-20 07:51:48 +03:00
bed7a1d3b5 fix: UserDatas, do now set modified option in second round 2025-06-20 07:51:35 +03:00
06c87718d6 bump: version 1.2.0a27 → 1.2.0a28 2025-06-18 16:50:18 +03:00
a0677e7b00 fix: conversion 2025-06-18 16:49:45 +03:00
c840d57734 bump: version 1.2.0a26 → 1.2.0a27 2025-06-18 07:49:44 +03:00
a3b2699289 feat: separate rougail and rougail-base 2025-06-18 07:49:39 +03:00
c7a66a034e bump: version 1.2.0a25 → 1.2.0a26 2025-05-26 08:11:43 +02:00
9e06f1142e fix: user_data better support for follower variable 2025-05-19 19:08:32 +02:00
b902e15243 bump: version 1.2.0a24 → 1.2.0a25 2025-05-14 08:44:37 +02:00
cbccc8087f feat: can launch UserDatas twice 2025-05-12 19:25:50 +02:00
fecab8aca6 bump: version 1.2.0a23 → 1.2.0a24 2025-05-12 09:13:57 +02:00
97cc2460b2 fix: upgrade translation 2025-05-12 08:52:59 +02:00
5a4bb343b0 fix: black 2025-05-12 08:45:39 +02:00
bb1f117ed1 bump: version 1.2.0a22 → 1.2.0a23 2025-05-09 08:21:40 +02:00
1662568c17 fix: add_quotes 2025-05-09 08:21:31 +02:00
6be773de3c fix: tiramisu_display_name could display only description 2025-05-08 22:10:28 +02:00
70b6f3d274 fix: use own undefined 2025-05-08 22:09:52 +02:00
e7a1caf113 fix: support of default_dictionary_format_version file in tests 2025-05-07 08:06:30 +02:00
b229154df5 fix: simplify version support 2025-05-06 08:09:37 +02:00
e71d4600d7 bump: version 1.2.0a21 → 1.2.0a22 2025-05-05 08:49:45 +02:00
adc580069c fix: user_datas support empty directory 2025-05-05 08:49:11 +02:00
9c940f8b28 bump: version 1.2.0a20 → 1.2.0a21 2025-05-02 08:12:31 +02:00
6167e1ea2c fix: support {{ suffix }} name in 1.1 format version 2025-05-02 08:12:06 +02:00
46e2ede81d fix: do not force use_data usage 2025-05-02 08:12:03 +02:00
2488cc471e fix: validators for an index 2025-05-01 21:27:19 +02:00
1dcc00429c bump: version 1.2.0a19 → 1.2.0a20 2025-04-30 09:02:51 +02:00
2f9e584394 fix: remove symlink 2025-04-30 09:02:16 +02:00
2027da0675 bump: version 1.2.0a18 → 1.2.0a19 2025-04-30 08:56:28 +02:00
efb23c350e fix: update translation 2025-04-29 23:12:32 +02:00
52c8277238 fix: remove negative_description support 2025-04-29 22:48:33 +02:00
3b0130420d fix: redefine family in flatten mode 2025-04-29 22:48:03 +02:00
00f29c37c4 feat: add ymlfile names in ConfigError message 2025-04-27 16:35:13 +02:00
b9ea5edb07 fix: update tests 2025-04-27 10:30:31 +02:00
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
1727 changed files with 15020 additions and 12094 deletions

View file

@ -1,3 +1,275 @@
## 1.2.0a35 (2025-10-02)
### Feat
- add warning class
## 1.2.0a34 (2025-09-30)
### Feat
- for formatter
## 1.2.0a33 (2025-09-29)
### Fix
- better doc for calculation with unknown variable
## 1.2.0a32 (2025-09-29)
### Feat
- **#28**: default value for a calculated variable with an unknown optional variable
- **#26**: cidr and network_cidr type is depreciate
- **#25**: add integer type which will replace number type
## 1.2.0a31 (2025-09-22)
### Fix
- dictionary => structure
## 1.2.0a30 (2025-09-20)
### Feat
- **40**: better conflict error message with dynamic name
- add new get_root_option function
### Fix
- update test
## 1.2.0a29 (2025-06-20)
### Fix
- UserDatas, do now set modified option in second round
## 1.2.0a28 (2025-06-18)
### Fix
- conversion
## 1.2.0a27 (2025-06-18)
### Feat
- separate rougail and rougail-base
## 1.2.0a26 (2025-05-26)
### Fix
- user_data better support for follower variable
## 1.2.0a25 (2025-05-14)
### Feat
- can launch UserDatas twice
## 1.2.0a24 (2025-05-12)
### Fix
- upgrade translation
- black
## 1.2.0a23 (2025-05-09)
### Fix
- add_quotes
- tiramisu_display_name could display only description
- use own undefined
- support of default_dictionary_format_version file in tests
- simplify version support
## 1.2.0a22 (2025-05-05)
### Fix
- user_datas support empty directory
## 1.2.0a21 (2025-05-02)
### Fix
- support {{ suffix }} name in 1.1 format version
- do not force use_data usage
- validators for an index
## 1.2.0a20 (2025-04-30)
### Fix
- remove symlink
## 1.2.0a19 (2025-04-30)
### Feat
- add ymlfile names in ConfigError message
### Fix
- update translation
- remove negative_description support
- redefine family in flatten mode
- update tests
- better multi check
## 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) ## 1.1.1 (2024-11-06)
### Fix ### Fix

View file

@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2024-10-30 13:21+0100\n" "POT-Creation-Date: 2025-05-12 08:45+0200\n"
"PO-Revision-Date: 2024-10-30 13:39+0100\n" "PO-Revision-Date: 2025-05-12 08:52+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n" "Language: fr\n"
@ -16,39 +16,39 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.5\n" "X-Generator: Poedit 3.5\n"
#: src/rougail/annotator/family.py:142 #: src/rougail/annotator/family.py:150
msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}" msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr "" msgstr ""
"le mode d'une variable par défaut \"{0}\" n'est pas un mode valide, les " "le mode d'une variable par défaut \"{0}\" n'est pas un mode valide, les "
"modes valides sont {1}" "modes valides sont {1}"
#: src/rougail/annotator/family.py:148 #: src/rougail/annotator/family.py:156
msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}" msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr "" msgstr ""
"le mode d'une famille par défaut \"{0}\" n'est pas un mode valide, les modes " "le mode d'une famille par défaut \"{0}\" n'est pas un mode valide, les modes "
"valides sont {1}" "valides sont {1}"
#: src/rougail/annotator/family.py:180 #: src/rougail/annotator/family.py:188
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}" msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}"
msgstr "" msgstr ""
"le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, les modes valides " "le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, les modes valides "
"sont {2}" "sont {2}"
#: src/rougail/annotator/family.py:184 #: src/rougail/annotator/family.py:192
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available" msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available"
msgstr "" msgstr ""
"le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, aucun mode ne sont " "le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, aucun mode ne sont "
"définis" "définis"
#: src/rougail/annotator/family.py:248 #: src/rougail/annotator/family.py:256
msgid "" msgid ""
"the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the " "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the "
"higher family mode \"{2}\"" "higher family mode \"{2}\""
msgstr "" msgstr ""
"la variable \"{0}\" est obligatoire donc dans le mode \"{1}\" mais la " "la variable \"{0}\" est obligatoire, donc en mode \"{1}\", mais la famille a "
"famille a un mode supérieur \"{2}\"" "un mode supérieur \"{2}\""
#: src/rougail/annotator/family.py:286 #: src/rougail/annotator/family.py:294
msgid "" msgid ""
"the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode " "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode "
"\"{2}\"" "\"{2}\""
@ -56,7 +56,7 @@ msgstr ""
"la variable suiveuse \"{0}\" a le mode \"{1}\" mais la variable leader a un " "la variable suiveuse \"{0}\" a le mode \"{1}\" mais la variable leader a un "
"mode supérieur \"{2}\"" "mode supérieur \"{2}\""
#: src/rougail/annotator/family.py:319 #: src/rougail/annotator/family.py:327
msgid "" msgid ""
"the family \"{0}\" is in \"{1}\" mode but variables and families inside have " "the family \"{0}\" is in \"{1}\" mode but variables and families inside have "
"the higher modes \"{2}\"" "the higher modes \"{2}\""
@ -64,7 +64,7 @@ msgstr ""
"la famille \"{0}\" a le mode \"{1}\" mais les variables et les familles à " "la famille \"{0}\" a le mode \"{1}\" mais les variables et les familles à "
"l'intérieur ont des modes supérieurs \"{2}\"" "l'intérieur ont des modes supérieurs \"{2}\""
#: src/rougail/annotator/family.py:337 #: src/rougail/annotator/family.py:345
msgid "" msgid ""
"the variable \"{0}\" is in \"{1}\" mode but family has the higher family " "the variable \"{0}\" is in \"{1}\" mode but family has the higher family "
"mode \"{2}\"" "mode \"{2}\""
@ -72,37 +72,369 @@ msgstr ""
"la variable \"{0}\" est dans le mode \"{1}\" mais la famille a le mode " "la variable \"{0}\" est dans le mode \"{1}\" mais la famille a le mode "
"supérieur \"{2}\"" "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" msgid ""
"the follower \"{0}\" is not multi, so cannot have a list has default value"
msgstr "" msgstr ""
"la variable suiveuse \"{0}\" sans l'attribut multi peut avoir seulement une " "la variable suiveuse \"{0}\" n'est pas multiple, donc ne peut avoir une "
"valeur" "liste comme valeur par défaut"
#: 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" msgid "the variable \"{0}\" is multi but has a non list default value"
msgstr "" msgstr ""
"la variable \"{0}\" est multiple mais a une valeur par défaut sans être une " "la variable \"{0}\" est multiple mais a une valeur par défaut sans être une "
"liste" "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:137
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:86
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:93
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:98
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:227
msgid "" msgid ""
"the variable \"{0}\" has regexp attribut but has not the \"regexp\" type" "the variable \"{0}\" has regexp attribut but has not the \"regexp\" type"
msgstr "" msgstr ""
"la variable \"{0}\" a un attribut regexp mais n'a pas le type \"regexp\"" "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:270
msgid "" msgid ""
"the variable \"{0}\" has choices attribut but has not the \"choice\" type" "the variable \"{0}\" has choices attribut but has not the \"choice\" type"
msgstr "" msgstr ""
"la variable \"{0}\" a un attribut choices mais n'a pas le type \"choice\"" "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:298
msgid "" msgid ""
"the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}" "the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}"
msgstr "" msgstr ""
"la variable \"{0}\" a la valeur par défaut invalide \"{1}\" devrait être {2}" "la variable \"{0}\" a la valeur par défaut invalide \"{1}\" devrait être {2}"
#: src/rougail/convert.py:268 #: src/rougail/config.py:226
msgid "Structure format version by default, if not specified in structure file"
msgstr ""
"La version du format de la structure par défaut, si non spécifier dans le "
"fichier de structure"
#: 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:312
msgid "Tiramisu cache filename"
msgstr "Nom du fichier du cache Tiramisu"
#: src/rougail/config.py:320
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:326
msgid "Name of extra annotators"
msgstr "Nom des annotators supplémentaires"
#: src/rougail/config.py:332
msgid "Suffix add to generated options name"
msgstr "Suffix ajouté pour généré le nom des options"
#: src/rougail/config.py:338
msgid "Every variables in calculation are optionals"
msgstr "Toutes les variables dans un calcul sont optionnelles"
#: src/rougail/config.py:342
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:349
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:350
msgid "The pattern is in Jinja format"
msgstr "Le patron est au format Jinja"
#: src/rougail/config.py:378
msgid "Select for {0}"
msgstr "Sélection pour {0}"
#: src/rougail/config.py:458
msgid "Override default parameters for option type"
msgstr "Sur charger les paramètre par défaut pour le type d'option"
#: 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:280
msgid "unknown type {0} for {1}"
msgstr "type {0} inconnu pour {1}"
#: src/rougail/convert.py:411
msgid "family \"{0}\" define multiple time"
msgstr "la famille \"{0}\" est définit plusieurs fois"
#: src/rougail/convert.py:668
msgid "variable \"{0}\" define multiple time"
msgstr "la variable \"{0}\" est définit plusieurs fois"
#: src/rougail/convert.py:766
msgid "params must be a dict for {0}"
msgstr "params doit être une dict pour {0}"
#: src/rougail/convert.py:787
msgid "\"{0}\" has an invalid \"params\" for {1}: {2}"
msgstr "\"{0}\" a un attribut \"params\" invalide pour {1}: {2}"
#: src/rougail/convert.py:801
msgid "secret_manager must be a dict for {0}"
msgstr "secret_manager doit être une dict pour {0}"
#: src/rougail/convert.py:1129
msgid "Cannot execute annotate multiple time"
msgstr "Ne peut exécuter l'annotation plusieurs fois"
#: src/rougail/convert.py:1136
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/tiramisu.py:60
#: src/rougail/user_datas.py:354 src/rougail/user_datas.py:357
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:187
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:193
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:198 src/rougail/object_model.py:525
msgid "unknown object \"{0}\" in attribute \"{1}\" for \"{2}\""
msgstr "objet inconnu \"{0}\" dans l'attribut \"{1}\" pour \"{2}\""
#: src/rougail/object_model.py:217
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:246
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:251
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:268
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:520
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:544
msgid ""
"variable \"{0}\" has an attribute \"{1}\" calculated with the unknown "
"variable \"{2}\""
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\" calculé avec la variable "
"inconnue \"{2}\""
#: src/rougail/object_model.py:593
msgid ""
"the variable \"{0}\" has an invalid \"{1}\" the variable \"{2}\" is in a sub "
"dynamic option"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable \"{2}\" est "
"dans une sous option dynamique"
#: src/rougail/object_model.py:602
msgid ""
"the leader \"{0}\" has an invalid \"{1}\" the follower \"{2}\" is a multi"
msgstr ""
"la variable meneuse \"{0}\" a un attribut invalide \"{1}\", la variable "
"suiveuse \"{2}\" est multiple"
#: src/rougail/object_model.py:644
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" "
"must not be multi"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable \"{2}\" ne "
"doit pas être multiple"
#: src/rougail/object_model.py:658
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable must not "
"be a multi or the variable \"{2}\" must be multi"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable ne doit pas "
"être multiple ou la variable \"{2}\" doit être multiple"
#: src/rougail/object_model.py:672
msgid ""
"the variable \"{0}\" has an invalid attribute \"{1}\", the variable must be "
"a multi or the variable \"{2}\" must not be multi"
msgstr ""
"la variable \"{0}\" a un attribut invalide \"{1}\", la variable doit être "
"multiple ou la variable \"{2}\" ne doit pas être multiple"
#: src/rougail/object_model.py:686
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:704
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:724
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:770
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:775
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:783
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:840
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:845
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:898
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:904 src/rougail/object_model.py:914
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:939
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:207
msgid "" msgid ""
"A variable or a family located in the \"{0}\" namespace shall not be used in " "A variable or a family located in the \"{0}\" namespace shall not be used in "
"the \"{1}\" namespace" "the \"{1}\" namespace"
@ -110,50 +442,142 @@ msgstr ""
"Une variable ou une famille localisé dans l'espace de nom \"{0}\" ne devrait " "Une variable ou une famille localisé dans l'espace de nom \"{0}\" ne devrait "
"pas être utilisé dans l'espace de nom \"{1}\"" "pas être utilisé dans l'espace de nom \"{1}\""
#: src/rougail/convert.py:462 #: src/rougail/structural_commandline/annotator.py:66
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
msgid "alternative_name \"{0}\" conflict with \"--help\"" msgid "alternative_name \"{0}\" conflict with \"--help\""
msgstr "alternative_name \"{0}\" est en conflit avec \"--help\"" msgstr "alternative_name \"{0}\" est en conflit avec \"--help\""
#: src/rougail/structural_commandline/annotator.py:73 #: src/rougail/structural_commandline/annotator.py:71
msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\"" msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\""
msgstr "conflit dans les \"alternative_name\" \"{0}\": \"{1}\" et \"{2}\"" msgstr "conflit dans les \"alternative_name\" \"{0}\": \"{1}\" et \"{2}\""
#: src/rougail/structural_commandline/annotator.py:96 #: src/rougail/structural_directory/__init__.py:127
msgid "" msgid "duplicate dictionary file name {0}"
"negative_description is mandatory for boolean variable, but \"{0}\" hasn't" msgstr "nom de fichier {0} de dictionnaire dupliqué"
#: 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:49
msgid "Sort dictionaries from differents directories"
msgstr "Trier les fichiers de structure à partir de différents répertoires"
#: src/rougail/structural_directory/config.py:58
msgid "Main namespace name"
msgstr "Nom de l'espace de nom principal"
#: src/rougail/structural_directory/config.py:69
msgid "Extra namespaces"
msgstr "Espaces de nom supplémentaires"
#: src/rougail/structural_directory/config.py:78
msgid "Extra namespace name"
msgstr "Nom de l'espace de nom supplémentaire"
#: src/rougail/structural_directory/config.py:84
msgid "Directories where extra dictionary files are placed"
msgstr "" msgstr ""
"l'attribut negative_description est obligatoire pour des variables " "Répertoires où sont placés les fichiers de structure de l'espace de nom "
"\"boolean\", mais \"{0}\" n'en a pas" "supplémentaire"
#: src/rougail/structural_commandline/annotator.py:105 #: src/rougail/tiramisu.py:187
msgid "" msgid ""
"negative_description is only available for boolean variable, but \"{0}\" is " "cannot calculating \"{0}\" attribute for variable \"{1}\" in {2} with "
"\"{1}\"" "parameters \"{3}\": {4}"
msgstr "" msgstr ""
"l'attribut negative_description est seulement valide pour des variables " "ne peut calculer l'attribut \"{0}\" pour la variable \"{1}\" dans {2} avec "
"\"boolean\", mais \"{0}\" est \"{1}\"" "les paramètres \"{3}\" : {4}"
#: src/rougail/update/update.py:741 #: src/rougail/tiramisureflector.py:363
msgid "not a XML file: {0}" msgid "internal error, {0} is not a dynamic variable"
msgstr "fichier XML invalid : {0}" msgstr "erreur interne, \"{0}\" n'est pas une variable dynamique"
#: src/rougail/utils.py:58 #: src/rougail/user_datas.py:118
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:196
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:218
msgid "loaded from {0}"
msgstr "chargée depuis {0}"
#: src/rougail/user_datas.py:258
msgid ""
"cannot set the value \"{0}\" to the family {1}, it will be ignored when "
"loading from {2}"
msgstr ""
"impossible de définir la valeur \"{0}\" à la famille {1}, elle sera ignorée "
"lors du chargement depuis {0}"
#: src/rougail/user_datas.py:270
msgid ""
"variable or family \"{0}\" does not exist, it will be ignored when loading "
"from {1}"
msgstr ""
"la variable ou la famille \"{0}\" n'existe pas, elle sera ignorée lors du "
"chargement depuis {1}"
#: src/rougail/user_datas.py:276
msgid ""
"\"{0}\" is the name of a dynamic family, it will be ignored when loading "
"from {1}"
msgstr ""
"\"{0}\" est le nom d'une famille dynamique, il sera ignoré lors du "
"chargement depuis {1}"
#: src/rougail/user_datas.py:282
msgid "{0} loaded from {1}"
msgstr "{0} chargée depuis {1}"
#: src/rougail/user_datas.py:311
msgid ""
"variable {0} at index \"{1}\" is {2}, it will be ignored when loading from "
"{3}"
msgstr ""
"la variable {0} à l'index \"{1}\" est {2}, elle sera ignorée lors du "
"chargement depuis {3}"
#: src/rougail/user_datas.py:322
msgid ""
"family {0} is {1}, {2} at index \"{3}\" will be ignored when loading from {4}"
msgstr ""
"la famille {0} est {1}, {2} à l'index \"{3}\" sera ignorée lors du "
"chargement depuis {4}"
#: src/rougail/user_datas.py:335
msgid "variable {0} is {1}, it will be ignored when loading from {2}"
msgstr ""
"la variable {0} est {1}, elle sera ignorée lors du chargement depuis {2}"
#: src/rougail/user_datas.py:343
msgid "family {0} is {1}, {2} will be ignored when loading from {3}"
msgstr "la famille {0} est {1}, {2} sera ignorée lors du chargement depuis {3}"
#: src/rougail/user_datas.py:362
msgid ""
"the value \"{0}\" is invalid for {1} at index \"{2}\", {3}, it will be "
"ignored when loading from {4}"
msgstr ""
"la valeur \"{0}\" n'est pas valide pour {1} à l'index \"{2}\", {3}, elle "
"sera ignorée lors du chargement depuis {4}"
#: src/rougail/user_datas.py:373
msgid ""
"the value \"{0}\" is invalid for {1}, {2}, it will be ignored when loading "
"from {3}"
msgstr ""
"la valeur \"{0}\" n'est pas valide pour {1}, {2}, elle sera ignorée lors du "
"chargement depuis {3}"
#: src/rougail/utils.py:55
msgid "" msgid ""
"invalid variable or family name \"{0}\" must only contains lowercase ascii " "invalid variable or family name \"{0}\" must only contains lowercase ascii "
"character, number or _" "character, number or _"
@ -161,6 +585,85 @@ msgstr ""
"nom invalide pour la variable ou famille \"{0}\" doit seulement contenir des " "nom invalide pour la variable ou famille \"{0}\" doit seulement contenir des "
"caractères ascii minuscule, nombre or _" "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}" msgid "error in jinja \"{0}\" for the variable \"{1}\": {2}"
msgstr "erreur dans Jinja \"{0}\" pour la variable \"{1}\": {2}" msgstr "erreur dans Jinja \"{0}\" pour la variable \"{1}\": {2}"
#~ msgid ""
#~ "the variable \"{0}\" is a family, so cannot set the value \"{1}\" in {2}"
#~ msgstr ""
#~ "la variable \"{0}\" est une famille, donc ne peut avoir l'avoir \"{1}\" "
#~ "dans {2}"
#~ 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"
#~ msgid "In cache file, do importation of Tiramisu and other dependencies"
#~ msgstr "Dans le fichier de cache, importer Tiramisu et autres dépendances"
#~ msgid "Variables in calculation are not optional by default"
#~ msgstr "Les variables dans un calcul sont optionnelles par défaut"
#~ 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}\""
#~ 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"
#~ 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"
#~ 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"
#~ msgid ""
#~ "negative_description is only available for boolean variable, but \"{0}\" "
#~ "is \"{1}\""
#~ msgstr ""
#~ "l'attribut negative_description est seulement valide pour des variables "
#~ "\"boolean\", mais \"{0}\" est \"{1}\""
#~ 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 "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-11-04 12:04+0100\n" "POT-Creation-Date: 2025-05-12 08:52+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,103 +15,396 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: src/rougail/annotator/family.py:139 #: src/rougail/annotator/family.py:150
msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}" msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:145 #: src/rougail/annotator/family.py:156
msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}" msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}"
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:177 #: src/rougail/annotator/family.py:188
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}" msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}"
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:181 #: src/rougail/annotator/family.py:192
msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available" msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available"
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:245 #: src/rougail/annotator/family.py:256
msgid "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the higher family mode \"{2}\"" msgid "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the higher family mode \"{2}\""
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:283 #: src/rougail/annotator/family.py:294
msgid "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode \"{2}\"" msgid "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode \"{2}\""
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:316 #: src/rougail/annotator/family.py:327
msgid "the family \"{0}\" is in \"{1}\" mode but variables and families inside have the higher modes \"{2}\"" msgid "the family \"{0}\" is in \"{1}\" mode but variables and families inside have the higher modes \"{2}\""
msgstr "" msgstr ""
#: src/rougail/annotator/family.py:334 #: src/rougail/annotator/family.py:345
msgid "the variable \"{0}\" is in \"{1}\" mode but family has the higher family mode \"{2}\"" msgid "the variable \"{0}\" is in \"{1}\" mode but family has the higher family mode \"{2}\""
msgstr "" 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" msgid "the follower \"{0}\" is not multi, so cannot have a list has default value"
msgstr "" 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" msgid "the variable \"{0}\" is multi but has a non list default value"
msgstr "" 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:137
msgid "the variable \"{0}\" is a \"regexp\" variable but don't have any regexp"
msgstr ""
#: src/rougail/annotator/variable.py:86
msgid "only \"unix_user\" or \"secret\" variable type can have \"secret_manager\" attribute, but \"{0}\" has type \"{1}\""
msgstr ""
#: src/rougail/annotator/variable.py:93
msgid "the variable \"{0}\" has attribute \"secret_manager\" but is a multi variable"
msgstr ""
#: src/rougail/annotator/variable.py:98
msgid "the variable \"{0}\" has attribute \"secret_manager\" so must not have default value"
msgstr ""
#: src/rougail/annotator/variable.py:227
msgid "the variable \"{0}\" has regexp attribut but has not the \"regexp\" type" msgid "the variable \"{0}\" has regexp attribut but has not the \"regexp\" type"
msgstr "" msgstr ""
#: src/rougail/annotator/variable.py:232 #: src/rougail/annotator/variable.py:270
msgid "the variable \"{0}\" has choices attribut but has not the \"choice\" type" msgid "the variable \"{0}\" has choices attribut but has not the \"choice\" type"
msgstr "" msgstr ""
#: src/rougail/annotator/variable.py:260 #: src/rougail/annotator/variable.py:298
msgid "the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}" msgid "the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}"
msgstr "" msgstr ""
#: src/rougail/convert.py:281 #: src/rougail/config.py:226
msgid "A variable or a family located in the \"{0}\" namespace shall not be used in the \"{1}\" namespace" msgid "Structure format version by default, if not specified in structure file"
msgstr "" msgstr ""
#: src/rougail/convert.py:475 #: src/rougail/config.py:234
msgid "File with functions"
msgstr ""
#: 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:312
msgid "Tiramisu cache filename"
msgstr ""
#: src/rougail/config.py:320
msgid "Name of internal functions that we can use as a function"
msgstr ""
#: src/rougail/config.py:326
msgid "Name of extra annotators"
msgstr ""
#: src/rougail/config.py:332
msgid "Suffix add to generated options name"
msgstr ""
#: src/rougail/config.py:338
msgid "Every variables in calculation are optionals"
msgstr ""
#: src/rougail/config.py:342
msgid "Loads redefine variables even if there don't already exists"
msgstr ""
#: src/rougail/config.py:349
msgid "The secret pattern to build item name in Bitwarden"
msgstr ""
#: src/rougail/config.py:350
msgid "The pattern is in Jinja format"
msgstr ""
#: src/rougail/config.py:378
msgid "Select for {0}"
msgstr ""
#: src/rougail/config.py:458
msgid "Override default parameters for option type"
msgstr ""
#: src/rougail/config.py:461
msgid "Default parameters for option type"
msgstr ""
#: src/rougail/convert.py:280
msgid "unknown type {0} for {1}" msgid "unknown type {0} for {1}"
msgstr "" msgstr ""
#: src/rougail/convert.py:1345 #: src/rougail/convert.py:411
msgid "duplicate dictionary file name {0}" msgid "family \"{0}\" define multiple time"
msgstr "" msgstr ""
#: src/rougail/convert.py:1392 #: src/rougail/convert.py:668
msgid "variable \"{0}\" define multiple time"
msgstr ""
#: src/rougail/convert.py:766
msgid "params must be a dict for {0}"
msgstr ""
#: src/rougail/convert.py:787
msgid "\"{0}\" has an invalid \"params\" for {1}: {2}"
msgstr ""
#: src/rougail/convert.py:801
msgid "secret_manager must be a dict for {0}"
msgstr ""
#: src/rougail/convert.py:1129
msgid "Cannot execute annotate multiple time" msgid "Cannot execute annotate multiple time"
msgstr "" msgstr ""
#: src/rougail/error.py:67 #: src/rougail/convert.py:1136
msgid "invalid \"structural\" definition ({0}), we cannot load any structural file!"
msgstr ""
#: src/rougail/error.py:67 src/rougail/tiramisu.py:60
#: src/rougail/user_datas.py:354 src/rougail/user_datas.py:357
msgid "{0} in {1}" msgid "{0} in {1}"
msgstr "" msgstr ""
#: src/rougail/structural_commandline/annotator.py:67 #: src/rougail/object_model.py:50
msgid "unknown boolean value \"{0}\""
msgstr ""
#: src/rougail/object_model.py:187
msgid "cannot find variable \"{0}\" defined in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:193
msgid "the variable \"{0}\" is in fact a family in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:198 src/rougail/object_model.py:525
msgid "unknown object \"{0}\" in attribute \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:217
msgid "identifier parameter for \"{0}\" in \"{1}\" cannot be set none dynamic family"
msgstr ""
#: src/rougail/object_model.py:246
msgid "cannot find variable \"{0}\" defined in \"{1}\" for \"{2}\""
msgstr ""
#: src/rougail/object_model.py:251
msgid "variable \"{0}\" defined in \"{1}\" for \"{2}\" is a dynamic variable"
msgstr ""
#: src/rougail/object_model.py:268
msgid "the variable \"{0}\" is not a follower, so cannot have index type for param in \"{1}\""
msgstr ""
#: src/rougail/object_model.py:520
msgid "a variable \"{0}\" is needs in attribute \"{1}\" for \"{2}\" but it's a family"
msgstr ""
#: src/rougail/object_model.py:544
msgid "variable \"{0}\" has an attribute \"{1}\" calculated with the unknown variable \"{2}\""
msgstr ""
#: src/rougail/object_model.py:593
msgid "the variable \"{0}\" has an invalid \"{1}\" the variable \"{2}\" is in a sub dynamic option"
msgstr ""
#: src/rougail/object_model.py:602
msgid "the leader \"{0}\" has an invalid \"{1}\" the follower \"{2}\" is a multi"
msgstr ""
#: src/rougail/object_model.py:644
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable \"{2}\" must not be multi"
msgstr ""
#: src/rougail/object_model.py:658
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable must not be a multi or the variable \"{2}\" must be multi"
msgstr ""
#: src/rougail/object_model.py:672
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", the variable must be a multi or the variable \"{2}\" must not be multi"
msgstr ""
#: src/rougail/object_model.py:686
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:704
msgid "\"{0}\" attribut shall not have an \"optional\" attribute for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:724
msgid "variable \"{0}\" has a default value calculated with \"{1}\" which has incompatible type"
msgstr ""
#: src/rougail/object_model.py:770
msgid "\"when\" is not allowed in format version 1.0 for attribute \"{0}\" for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:775
msgid "the variable \"{0}\" has an invalid attribute \"{1}\", \"when\" and \"when_not\" cannot set together"
msgstr ""
#: src/rougail/object_model.py:783
msgid "\"when_not\" is not allowed in format version 1.0 for attribute \"{0}\" for variable \"{1}\""
msgstr ""
#: src/rougail/object_model.py:840
msgid "cannot find variable \"{0}\" for the information \"{1}\" when calculating \"{2}\""
msgstr ""
#: src/rougail/object_model.py:845
msgid "identifier not allowed for the information \"{0}\" when calculating \"{1}\""
msgstr ""
#: src/rougail/object_model.py:898
msgid "\"when\" is not allowed in format version 1.0 for attribute \"{0}\""
msgstr ""
#: src/rougail/object_model.py:904 src/rougail/object_model.py:914
msgid "the identifier has an invalid attribute \"{0}\", \"when\" and \"when_not\" cannot set together"
msgstr ""
#: src/rougail/object_model.py:939
msgid "the variable \"{0}\" is not a follower, so cannot have index type for \"{1}\""
msgstr ""
#: src/rougail/path.py:207
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:66
msgid "alternative_name \"{0}\" conflict with \"--help\"" msgid "alternative_name \"{0}\" conflict with \"--help\""
msgstr "" msgstr ""
#: src/rougail/structural_commandline/annotator.py:72 #: src/rougail/structural_commandline/annotator.py:71
msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\"" msgid "conflict alternative_name \"{0}\": \"{1}\" and \"{2}\""
msgstr "" msgstr ""
#: src/rougail/structural_commandline/annotator.py:95 #: src/rougail/structural_directory/__init__.py:127
msgid "negative_description is mandatory for boolean variable, but \"{0}\" hasn't" msgid "duplicate dictionary file name {0}"
msgstr "" msgstr ""
#: src/rougail/structural_commandline/annotator.py:104 #: src/rougail/structural_directory/config.py:32
msgid "negative_description is only available for boolean variable, but \"{0}\" is \"{1}\"" msgid "Directories where dictionary files are placed"
msgstr "" msgstr ""
#: src/rougail/update/update.py:738 #: src/rougail/structural_directory/config.py:49
msgid "not a XML file: {0}" msgid "Sort dictionaries from differents directories"
msgstr ""
#: src/rougail/structural_directory/config.py:58
msgid "Main namespace name"
msgstr ""
#: src/rougail/structural_directory/config.py:69
msgid "Extra namespaces"
msgstr ""
#: src/rougail/structural_directory/config.py:78
msgid "Extra namespace name"
msgstr ""
#: src/rougail/structural_directory/config.py:84
msgid "Directories where extra dictionary files are placed"
msgstr ""
#: src/rougail/tiramisu.py:187
msgid "cannot calculating \"{0}\" attribute for variable \"{1}\" in {2} with parameters \"{3}\": {4}"
msgstr ""
#: src/rougail/tiramisureflector.py:363
msgid "internal error, {0} is not a dynamic variable"
msgstr ""
#: src/rougail/user_datas.py:118
msgid "cannot load variable path \"{0}\", the identifier \"{1}\" is not valid in {2}"
msgstr ""
#: src/rougail/user_datas.py:196
msgid "the variable \"{0}\" contains secrets and should not be defined in {1}"
msgstr ""
#: src/rougail/user_datas.py:218
msgid "loaded from {0}"
msgstr ""
#: src/rougail/user_datas.py:258
msgid "cannot set the value \"{0}\" to the family {1}, it will be ignored when loading from {2}"
msgstr ""
#: src/rougail/user_datas.py:270
msgid "variable or family \"{0}\" does not exist, it will be ignored when loading from {1}"
msgstr ""
#: src/rougail/user_datas.py:276
msgid "\"{0}\" is the name of a dynamic family, it will be ignored when loading from {1}"
msgstr ""
#: src/rougail/user_datas.py:282
msgid "{0} loaded from {1}"
msgstr ""
#: src/rougail/user_datas.py:311
msgid "variable {0} at index \"{1}\" is {2}, it will be ignored when loading from {3}"
msgstr ""
#: src/rougail/user_datas.py:322
msgid "family {0} is {1}, {2} at index \"{3}\" will be ignored when loading from {4}"
msgstr ""
#: src/rougail/user_datas.py:335
msgid "variable {0} is {1}, it will be ignored when loading from {2}"
msgstr ""
#: src/rougail/user_datas.py:343
msgid "family {0} is {1}, {2} will be ignored when loading from {3}"
msgstr ""
#: src/rougail/user_datas.py:362
msgid "the value \"{0}\" is invalid for {1} at index \"{2}\", {3}, it will be ignored when loading from {4}"
msgstr ""
#: src/rougail/user_datas.py:373
msgid "the value \"{0}\" is invalid for {1}, {2}, it will be ignored when loading from {3}"
msgstr "" msgstr ""
#: src/rougail/utils.py:55 #: src/rougail/utils.py:55
msgid "invalid variable or family name \"{0}\" must only contains lowercase ascii character, number or _" msgid "invalid variable or family name \"{0}\" must only contains lowercase ascii character, number or _"
msgstr "" msgstr ""
#: src/rougail/utils.py:117 #: src/rougail/utils.py:113
msgid "error in jinja \"{0}\" for the variable \"{1}\": {2}" msgid "error in jinja \"{0}\" for the variable \"{1}\": {2}"
msgstr "" msgstr ""

View file

@ -1,50 +1,18 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "rougail" name = "rougail"
version = "1.1.1" version = "1.2.0a35"
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"
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
"ruamel.yaml ~= 0.18.6",
"pydantic ~= 2.9.2",
"jinja2 ~= 3.1.4",
"tiramisu >=5.0,<6"
]
[project.optional-dependencies]
dev = [
"pylint ~= 3.0.3",
"pytest ~= 8.2.2",
"lxml ~= 5.2.2"
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail"
[tool.commitizen] [tool.commitizen]
name = "cz_conventional_commits" name = "cz_conventional_commits"
tag_format = "$version" tag_format = "$version"
version_scheme = "pep440" version_scheme = "pep440"
version_provider = "pep621" version_provider = "pep621"
version_files = [
"src/rougail/__version__.py",
"rougail-pyproject.toml:version",
"rougail-pyproject.toml:rougail-base == ",
"rougail-base-pyproject.toml:version",
]
update_changelog_on_bump = true update_changelog_on_bump = true
changelog_merge_prerelease = true changelog_merge_prerelease = true

View file

@ -0,0 +1,52 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail-base"
version = "1.2.0a35"
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.10"
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"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",
"Natural Language :: French",
]
dependencies = [
"jinja2 ~= 3.1.4",
"tiramisu ~= 5.0"
]
[project.optional-dependencies]
dev = [
"pylint ~= 3.0.3",
"pytest ~= 8.2.2",
]
[tool.flit.module]
name = "rougail"
[tool.flit.sdist]
exclude = [
"src/rougail/annotator",
"src/rougail/config",
"src/rougail/convert",
"src/rougail/structural_commandline",
"src/rougail/structural_directory",
"src/rougail/update",
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail"

35
rougail-pyproject.toml Normal file
View file

@ -0,0 +1,35 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail"
version = "1.2.0a35"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
description = "A consistency handling system that was initially designed in the configuration management"
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
"ruamel.yaml ~= 0.18.6",
"pydantic ~= 2.9.2",
"rougail-base == 1.2.0a35",
]
[tool.flit.sdist]
exclude = [
"src/rougail/error.py",
"src/rougail/i18n.py",
"src/rougail/tiramisu.py",
"src/rougail/user_datas.py",
"src/rougail/utils.py",
"src/rougail/__version__.py",
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail"

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -25,302 +25,12 @@ 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from tiramisu import Config, undefined from .__version__ import __version__
from tiramisu.error import PropertiesOptionError, LeadershipError, ConfigError
from warnings import warn
from typing import List
from re import compile, findall
from .convert import RougailConvert try:
from .config import RougailConfig from .convert import Rougail
from .update import RougailUpgrade from .config import RougailConfig
from .object_model import CONVERT_OPTION
from .utils import normalize_family
__all__ = ("Rougail", "RougailConfig", "__version__")
def tiramisu_display_name( except ModuleNotFoundError as err:
kls, __all__ = ("__version__",)
subconfig,
with_quote: bool = False,
) -> str:
"""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:
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
path = kls.impl_getpath()
if "{{ identifier }}" in path and subconfig.identifiers:
path = path.replace(
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
)
if with_quote:
return f'"{path}"{comment}'
return f"{path}{comment}"
class Rougail:
"""Main Rougail object"""
def __init__(
self,
rougailconfig=None,
) -> None:
if rougailconfig is None:
rougailconfig = RougailConfig
self.rougailconfig = rougailconfig
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"])
optiondescription = {}
custom_types = {
custom.__name__: custom
for custom in self.rougailconfig["custom_types"].values()
}
exec(tiram_obj, custom_types, optiondescription) # pylint: disable=W0122
self.config = Config(
optiondescription["option_0"],
display_name=tiramisu_display_name,
)
self.config.property.read_write()
return self.config
def get_config(self):
warn(
"get_config is deprecated, use run instead",
DeprecationWarning,
stacklevel=2,
)
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")

View file

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

View file

@ -1,4 +1,4 @@
"""Annotate dictionaries """Annotate structural file
Created by: Created by:
EOLE (http://eole.orion.education.fr) EOLE (http://eole.orion.education.fr)
@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -71,16 +71,38 @@ class SpaceAnnotator: # pylint: disable=R0903
if extra_annotator in ANNOTATORS: if extra_annotator in ANNOTATORS:
continue continue
get_annotators(ANNOTATORS, extra_annotator) get_annotators(ANNOTATORS, extra_annotator)
for plugin in objectspace.plugins: for structural in objectspace.structurals:
try: 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: except ModuleNotFoundError:
pass pass
annotators = ANNOTATORS["rougail.annotator"].copy() annotators = ANNOTATORS["rougail.annotator"].copy()
for extra_annotator in objectspace.extra_annotators: for extra_annotator in objectspace.extra_annotators:
annotators.extend(ANNOTATORS[extra_annotator]) annotators.extend(ANNOTATORS[extra_annotator])
for plugin in objectspace.plugins: for structural in objectspace.structurals:
annotators.extend(ANNOTATORS[f"rougail.{plugin}.annotator"]) 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) annotators = sorted(annotators, key=get_level)
functions = {} functions = {}
functions_files = objectspace.functions_files functions_files = objectspace.functions_files

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -29,7 +29,7 @@ from typing import Optional
from rougail.i18n import _ from rougail.i18n import _
from rougail.error import DictConsistencyError from rougail.error import DictConsistencyError
from rougail.annotator.variable import Walk from rougail.annotator.variable import Walk
from rougail.object_model import VariableCalculation from rougail.convert.object_model import VariableCalculation
class Mode: # pylint: disable=R0903 class Mode: # pylint: disable=R0903
@ -125,11 +125,22 @@ class Annotator(Walk):
if family.type == "dynamic" and isinstance( if family.type == "dynamic" and isinstance(
family.dynamic, VariableCalculation family.dynamic, VariableCalculation
): ):
path = self.objectspace.paths.get_full_path( path = family.dynamic.variable
family.dynamic.variable, if (
family.path, 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) self.objectspace.informations.add(family.path, "dynamic_variable", path)
self.objectspace.informations.add(family.path, "ymlfiles", family.xmlfiles)
def change_modes(self): def change_modes(self):
"""change the mode of variables""" """change the mode of variables"""
@ -333,7 +344,7 @@ class Annotator(Walk):
if self._has_mode(variable): if self._has_mode(variable):
msg = _( msg = _(
'the variable "{0}" is in "{1}" mode but family has the higher family mode "{2}"' '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) raise DictConsistencyError(msg, 61, variable.xmlfiles)
self._set_auto_mode(variable, family_mode) self._set_auto_mode(variable, family_mode)
if not variable.mode: if not variable.mode:

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -29,7 +29,7 @@ from typing import Union
from rougail.i18n import _ from rougail.i18n import _
from rougail.error import DictConsistencyError from rougail.error import DictConsistencyError
from rougail.annotator.variable import Walk from rougail.annotator.variable import Walk
from rougail.object_model import Calculation from rougail.convert.object_model import Calculation
PROPERTIES = ( PROPERTIES = (
@ -120,7 +120,7 @@ class Annotator(Walk):
else: else:
value = [] value = []
for calculation in frozen: for calculation in frozen:
calculation_copy = calculation.copy() calculation_copy = calculation.model_copy()
calculation_copy.attribute_name = "frozen" calculation_copy.attribute_name = "frozen"
calculation_copy.ori_path = calculation_copy.path calculation_copy.ori_path = calculation_copy.path
calculation_copy.path = path calculation_copy.path = path

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -29,7 +29,7 @@ from rougail.annotator.variable import Walk
from rougail.i18n import _ from rougail.i18n import _
from rougail.error import DictConsistencyError from rougail.error import DictConsistencyError
from rougail.object_model import Calculation from rougail.convert.object_model import Calculation
class Annotator(Walk): # pylint: disable=R0903 class Annotator(Walk): # pylint: disable=R0903
@ -47,6 +47,7 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace = objectspace self.objectspace = objectspace
self.convert_value() self.convert_value()
self.valid_choices() self.valid_choices()
self.valid_regexp()
def convert_value(self) -> None: def convert_value(self) -> None:
"""convert value""" """convert value"""
@ -75,8 +76,8 @@ class Annotator(Walk): # pylint: disable=R0903
raise DictConsistencyError(msg, 68, variable.xmlfiles) raise DictConsistencyError(msg, 68, variable.xmlfiles)
if variable.path in self.objectspace.followers and multi != "submulti": if variable.path in self.objectspace.followers and multi != "submulti":
msg = _( msg = _(
'the follower "{0}" without multi attribute can only have one value' 'the follower "{0}" is not multi, so cannot have a list has default value'
).format(variable.name) ).format(variable.path)
raise DictConsistencyError(msg, 87, variable.xmlfiles) raise DictConsistencyError(msg, 87, variable.xmlfiles)
if not variable.default: if not variable.default:
variable.default = None variable.default = None
@ -92,7 +93,7 @@ class Annotator(Walk): # pylint: disable=R0903
elif variable.multi: elif variable.multi:
msg = _( msg = _(
'the variable "{0}" is multi but has a non list default value' 'the variable "{0}" is multi but has a non list default value'
).format(variable.name) ).format(variable.path)
raise DictConsistencyError(msg, 12, variable.xmlfiles) raise DictConsistencyError(msg, 12, variable.xmlfiles)
elif variable.path in self.objectspace.followers: elif variable.path in self.objectspace.followers:
self.objectspace.default_multi[variable.path] = variable.default self.objectspace.default_multi[variable.path] = variable.default
@ -114,7 +115,9 @@ class Annotator(Walk): # pylint: disable=R0903
if isinstance(variable.choices, Calculation): if isinstance(variable.choices, Calculation):
continue continue
if variable.choices is None: 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) raise DictConsistencyError(msg, 19, variable.xmlfiles)
if not variable.mandatory and not variable.multi: if not variable.mandatory and not variable.multi:
self.add_choice_nil(variable) self.add_choice_nil(variable)
@ -125,3 +128,13 @@ class Annotator(Walk): # pylint: disable=R0903
if choice is None: if choice is None:
return return
variable.choices.append(None) 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 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -25,10 +25,14 @@ 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from rougail.i18n import _
from rougail.error import DictConsistencyError
from rougail.object_model import Calculation, VariableCalculation
from tiramisu.error import display_list 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.convert.object_model import Calculation, VariableCalculation, IndexCalculation
from rougail.tiramisu import display_xmlfiles, RENAME_TYPE
from warnings import warn
class Walk: class Walk:
@ -62,42 +66,86 @@ class Annotator(Walk): # pylint: disable=R0903
if not objectspace.paths: if not objectspace.paths:
return return
self.objectspace = objectspace 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` # default type inference from a default value with :term:`basic types`
self.basic_types = { self.basic_types = {
str: "string", str: "string",
int: "number", int: "integer",
bool: "boolean", bool: "boolean",
float: "float", float: "float",
} }
self.verify_secret_managers()
self.verify_choices() self.verify_choices()
self.convert_variable() self.convert_variable()
self.convert_test() self.convert_test()
self.convert_examples() self.convert_examples()
self.convert_help() 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): def convert_variable(self):
"""convert variable""" """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(): for variable in self.get_variables():
if variable.type == "symlink": if variable.type == "symlink":
continue continue
if variable.version != "1.0": if variable.version != "1.0":
self._default_variable_copy_informations(variable) self._convert_variable_inference(variable)
if variable.multi is None: self._convert_variable_multi(variable)
variable.multi = False for variable in self.get_variables():
if variable.type is None: if variable.type == "symlink":
variable.type = "string" continue
self.objectspace.informations.add(variable.path, "type", variable.type) if variable.type in RENAME_TYPE:
warning = f'the variable "{ variable.path }" has a depreciated type "{variable.type}", please use "{RENAME_TYPE[variable.type]}" instead in {display_xmlfiles(variable.xmlfiles)}'
warn(warning,
DeprecationWarning,
)
variable.type = RENAME_TYPE[variable.type]
if variable.type == 'cidr':
warning = f'the variable "{ variable.path }" has a depreciated type "{variable.type}", please use type "ip" with attribute cidr=True instead in {display_xmlfiles(variable.xmlfiles)}'
warn(warning,
DeprecationWarning,
)
if variable.type == 'network_cidr':
warning = f'the variable "{ variable.path }" has a depreciated type "{variable.type}", please use type "network" with attribute cidr=True instead in {display_xmlfiles(variable.xmlfiles)}'
warn(warning,
DeprecationWarning,
)
self.objectspace.informations.add(
variable.path, "ymlfiles", variable.xmlfiles
)
if variable.version != "1.0" and variable.type is None:
if isinstance(
variable.default, IndexCalculation
):
variable.type = 'integer'
elif isinstance(
variable.default, VariableCalculation
):
calculated_variable_path, calculated_variable, identifier = (
variable.default.get_variable(self.objectspace)
)
if calculated_variable is not None:
self._default_variable_copy_informations(variable, calculated_variable)
self._convert_variable(variable) self._convert_variable(variable)
def _convert_variable_inference( def _convert_variable_inference(
@ -105,62 +153,77 @@ class Annotator(Walk): # pylint: disable=R0903
variable, variable,
) -> None: ) -> None:
# variable has no type # variable has no type
if variable.type is None: if variable.type is not None:
# choice type inference from the `choices` attribute return
if variable.choices is not None: # choice type inference from the `choices` attribute
variable.type = "choice" if variable.choices is not None:
elif variable.regexp is not None: variable.type = "choice"
variable.type = "regexp" elif variable.regexp is not None:
elif variable.default not in [None, []]: variable.type = "regexp"
if isinstance(variable.default, list): elif variable.default not in [None, []]:
tested_value = variable.default[0] if isinstance(variable.default, list):
else: tested_value = variable.default[0]
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
else: 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
):
calculated_variable_path, calculated_variable, identifier = (
variable.default.get_variable(self.objectspace)
)
if calculated_variable is not None:
if calculated_variable.multi is None:
if (
isinstance(calculated_variable.default, VariableCalculation)
and variable.path == calculated_variable.default.path
):
msg = _(
'the "{0}" default value is a calculation with itself'.format(
variable.path
)
)
raise DictConsistencyError(msg, 75, variable.xmlfiles)
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( def _default_variable_copy_informations(
self, self,
variable, variable,
calculated_variable,
) -> None: ) -> 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
):
return
# copy type and params # 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 variable.type = calculated_variable.type
if variable.params is None and calculated_variable.params is not None: if variable.params is None and calculated_variable.params is not None:
variable.params = calculated_variable.params variable.params = calculated_variable.params
# copy multi attribut if variable.type == "choice" and variable.choices is None:
if variable.multi is None: variable.choices = calculated_variable.choices
calculated_path = calculated_variable.path if variable.type == "regexp" and variable.regexp is None:
if ( variable.regexp = calculated_variable.regexp
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
def _convert_variable( def _convert_variable(
self, self,
@ -169,6 +232,9 @@ class Annotator(Walk): # pylint: disable=R0903
# variable without description: description is the name # variable without description: description is the name
if not variable.description: if not variable.description:
variable.description = variable.name 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 variable.path in self.objectspace.followers:
if not variable.multi: if not variable.multi:
self.objectspace.multis[variable.path] = True self.objectspace.multis[variable.path] = True
@ -177,8 +243,6 @@ class Annotator(Walk): # pylint: disable=R0903
elif variable.multi: elif variable.multi:
self.objectspace.multis[variable.path] = True self.objectspace.multis[variable.path] = True
if variable.path in self.objectspace.leaders: 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]] family = self.objectspace.paths[variable.path.rsplit(".", 1)[0]]
if variable.hidden: if variable.hidden:
family.hidden = variable.hidden family.hidden = variable.hidden

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -29,12 +29,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from pathlib import Path from pathlib import Path
from tiramisu import Config from tiramisu import Config
from ruamel.yaml import YAML from ruamel.yaml import YAML
from .utils import _, load_modules, normalize_family from ..utils import _, load_modules
from .convert import RougailConvert from ..tiramisu import normalize_family
from ..convert import RougailConvert
from ..convert.object_model import get_convert_option_types
RENAMED = { RENAMED = {
"dictionaries_dir": "main_dictionaries", "dictionaries_dir": "main_structural_directories",
"main_dictionaries": "main_structural_directories",
"variable_namespace": "main_namespace", "variable_namespace": "main_namespace",
"functions_file": "functions_files", "functions_file": "functions_files",
} }
@ -48,7 +51,7 @@ def get_sub_modules():
global SUBMODULES global SUBMODULES
if SUBMODULES is None: if SUBMODULES is None:
SUBMODULES = {} SUBMODULES = {}
for submodule in Path(__file__).parent.iterdir(): for submodule in Path(__file__).parent.parent.iterdir():
if submodule.name.startswith("_") or not submodule.is_dir(): if submodule.name.startswith("_") or not submodule.is_dir():
continue continue
config_file = submodule / "config.py" config_file = submodule / "config.py"
@ -60,43 +63,63 @@ def get_sub_modules():
def get_level(module): 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: 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.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.root = root
self.config = Config( self.config = Config(
self.root, self.root,
) )
self.config.property.read_only()
self.extra_vars = extra_vars self.extra_vars = extra_vars
self.not_in_tiramisu = NOT_IN_TIRAMISU | extra_vars self.not_in_tiramisu = NOT_IN_TIRAMISU | extra_vars
for variable, default_value in self.not_in_tiramisu.items(): for variable, default_value in self.not_in_tiramisu.items():
if not isinstance(default_value, str): if not isinstance(default_value, str):
default_value = default_value.copy() default_value = default_value.copy()
setattr(self, variable, default_value) setattr(self, variable, default_value)
self.config.property.read_only()
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
def __setitem__( def __setitem__(
self, self,
key, key,
value, value,
) -> None: ) -> None:
if self.root is None:
self.generate_config()
if key in self.not_in_tiramisu: if key in self.not_in_tiramisu:
setattr(self, key, value) setattr(self, key, value)
else: else:
@ -122,6 +145,8 @@ class _RougailConfig:
self, self,
key, key,
) -> None: ) -> None:
if self.root is None:
self.generate_config()
if key in self.not_in_tiramisu: if key in self.not_in_tiramisu:
return getattr(self, key) return getattr(self, key)
if key == "export_with_import": if key == "export_with_import":
@ -154,6 +179,8 @@ class _RougailConfig:
yield f"{option.path()}: {option.value.get()}" yield f"{option.path()}: {option.value.get()}"
def __repr__(self): def __repr__(self):
if self.root is None:
self.generate_config()
self.config.property.read_write() self.config.property.read_write()
try: try:
values = "\n".join(self.parse(self.config)) values = "\n".join(self.parse(self.config))
@ -172,7 +199,7 @@ class FakeRougailConvert(RougailConvert):
super().__init__({}) super().__init__({})
def load_config(self) -> None: def load_config(self) -> None:
self.sort_dictionaries_all = False self.sort_structural_files_all = False
self.main_namespace = None self.main_namespace = None
self.suffix = "" self.suffix = ""
self.custom_types = {} self.custom_types = {}
@ -182,103 +209,29 @@ class FakeRougailConvert(RougailConvert):
self.base_option_name = "baseoption" self.base_option_name = "baseoption"
self.export_with_import = True self.export_with_import = True
self.internal_functions = [] 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.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, backward_compatibility: bool = True,
add_extra_options: bool = True, add_extra_options: bool = True,
) -> _RougailConfig: ) -> "OptionDescription":
if backward_compatibility: rougail_options = f"""default_structural_format_version:
main_namespace_default = "rougail" description: {_('Structure format version by default, if not specified in structure file')}
else:
main_namespace_default = "null"
rougail_options = f"""default_dictionary_format_version:
description: Dictionary format version by default, if not specified in dictionary file
alternative_name: v alternative_name: v
choices: choices:
- '1.0' - '1.0'
- '1.1' - '1.1'
mandatory: false 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: functions_files:
description: File with functions description: {_("File with functions")}
alternative_name: c alternative_name: c
type: unix_filename type: unix_filename
params: params:
@ -290,74 +243,73 @@ functions_files:
mandatory: false mandatory: false
modes_level: modes_level:
description: All modes level available description: {_("All modes level available")}
multi: true multi: true
mandatory: false mandatory: false
""" """
if backward_compatibility: if backward_compatibility:
rougail_options += """ rougail_options += """ default:
default:
- basic - basic
- standard - standard
- advanced - advanced
""" """
rougail_options += """ rougail_options += f"""
default_family_mode: default_family_mode:
description: Default mode for a family description: {_("Default mode for a family")}
default: default:
jinja: | jinja: |
{% if modes_level %} {{% if modes_level %}}
{{ modes_level[0] }} {{{{ modes_level[0] }}}}
{% endif %} {{% endif %}}
disabled: disabled:
jinja: | jinja: |
{% if not modes_level %} {{% if not modes_level %}}
No mode No mode
{% endif %} {{% endif %}}
validators: validators:
- type: jinja - type: jinja
jinja: | jinja: |
{% if default_family_mode not in modes_level %} {{% if default_family_mode not in modes_level %}}
not in modes_level ({modes_level}) not in modes_level ({{modes_level}})
{% endif %} {{% endif %}}
commandline: false commandline: false
default_variable_mode: default_variable_mode:
description: Default mode for a variable description: {_("Default mode for a variable")}
default: default:
jinja: | jinja: |
{% if modes_level %} {{% if modes_level %}}
{% if modes_level | length == 1 %} {{% if modes_level | length == 1 %}}
{{ modes_level[0] }} {{{{ modes_level[0] }}}}
{% else %} {{% else %}}
{{ modes_level[1] }} {{{{ modes_level[1] }}}}
{% endif %} {{% endif %}}
{% endif %} {{% endif %}}
disabled: disabled:
jinja: | jinja: |
{% if not modes_level %} {{% if not modes_level %}}
No mode No mode
{% endif %} {{% endif %}}
validators: validators:
- type: jinja - type: jinja
jinja: | jinja: |
{% if default_variable_mode not in modes_level %} {{% if default_variable_mode not in modes_level %}}
not in modes_level ({modes_level}) not in modes_level ({{modes_level}})
{% endif %} {{% endif %}}
commandline: false commandline: false
base_option_name: base_option_name:
description: Option name for the base option description: {_("Option name for the base option")}
default: baseoption default: baseoption
commandline: false commandline: false
not_export_with_import: 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")}
default: false default: false
commandline: false commandline: false
tiramisu_cache: tiramisu_cache:
description: Tiramisu cache filename description: {_("Tiramisu cache filename")}
alternative_name: t alternative_name: t
type: unix_filename type: unix_filename
mandatory: false mandatory: false
@ -365,77 +317,119 @@ tiramisu_cache:
allow_relative: true allow_relative: true
internal_functions: 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 multi: true
mandatory: false mandatory: false
commandline: false commandline: false
extra_annotators: extra_annotators:
description: Name of extra annotators description: {_("Name of extra annotators")}
multi: true
mandatory: false
commandline: false
plugins:
description: Name of Rougail plugins
multi: true multi: true
mandatory: false mandatory: false
commandline: false commandline: false
suffix: suffix:
description: Suffix add to generated option name description: {_("Suffix add to generated options name")}
default: '' default: ''
mandatory: false mandatory: false
commandline: false commandline: false
force_optional:
description: {_("Every variables in calculation are optionals")}
default: False
load_unexist_redefine:
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 = { processes = {
"structural": [], "structural": [],
"output": [],
"user data": [], "user data": [],
"output": [],
} }
processes_empty = []
for module in get_sub_modules().values(): for module in get_sub_modules().values():
data = module.get_rougail_config() data = module.get_rougail_config(backward_compatibility=backward_compatibility)
processes[data["process"]].append(data) if data["process"]:
processes[data["process"]].append(data)
else:
processes_empty.append(data["options"])
# reorder # reorder
for process in processes: for process in processes:
processes[process] = list(sorted(processes[process], key=get_level)) processes[process] = list(sorted(processes[process], key=get_level))
rougail_process = """step: # Load and exporter steps rougail_process = "step: # Load and exporter steps"
disabled:
variable: upgrade"""
for process in processes: for process in processes:
if processes[process]: if processes[process]:
objects = processes[process] objects = processes[process]
rougail_process += """ process_name = normalize_family(process)
{NAME}: tr_process_name = _(process_name)
description: Select for {NAME} rougail_process += f"""
{process_name}:
description: {_('Select for {0}').format(tr_process_name)}
"""
if process != "structural":
rougail_process += """
alternative_name: {NAME[0]} alternative_name: {NAME[0]}
choices:
""".format( """.format(
NAME=normalize_family(process), NAME=normalize_family(process),
) )
rougail_process += """
choices:
"""
for obj in objects: for obj in objects:
rougail_process += f" - {obj['name']}\n" rougail_process += f" - {obj['name']}\n"
if process == "structural": 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": elif process == "user data":
rougail_process += """ multi: true rougail_process += """ multi: true
mandatory: false mandatory: false"""
"""
hidden_outputs = [ hidden_outputs = [
process["name"] process["name"]
for process in processes["output"] for process in processes["output"]
if not process.get("allow_user_data", True) if not process.get("allow_user_data", True)
] ]
if hidden_outputs: if hidden_outputs:
rougail_process += """ hidden: rougail_process += """
disabled:
type: jinja type: jinja
jinja: | jinja: |
""" """
for hidden_output in hidden_outputs: 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 Cannot load user data for NAME output
{% endif %} {% endif %}""".replace(
""".replace(
"NAME", hidden_output "NAME", hidden_output
) )
elif objects: elif objects:
@ -443,10 +437,10 @@ suffix:
DEFAULT=objects[0]["name"] DEFAULT=objects[0]["name"]
) )
else: else:
if process == 'output': if process == "output":
prop = 'hidden' prop = "hidden"
else: else:
prop = 'disabled' prop = "disabled"
rougail_process += """ rougail_process += """
{NAME}: {NAME}:
description: Select for {NAME} description: Select for {NAME}
@ -460,9 +454,32 @@ suffix:
NAME=normalize_family(process), NAME=normalize_family(process),
PROP=prop, PROP=prop,
) )
rougail_process += f"""
define_default_params: false # {_('Override default parameters for option type')}
default_params:
description: {_("Default parameters for option type")}
disabled:
variable: _.define_default_params
when: false
"""
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}
"""
# print(rougail_process)
rougail_options += rougail_process rougail_options += rougail_process
convert = FakeRougailConvert(add_extra_options) convert = FakeRougailConvert(add_extra_options)
convert._init() convert.init()
convert.namespace = None convert.namespace = None
convert.parse_root_file( convert.parse_root_file(
"rougail.config", "rougail.config",
@ -470,27 +487,48 @@ suffix:
"1.1", "1.1",
YAML().load(rougail_options), YAML().load(rougail_options),
) )
for process_empty in processes_empty:
convert.parse_root_file(
"rougail.config",
"",
"1.1",
YAML().load(process_empty),
)
extra_vars = {} extra_vars = {}
for process in processes: objects = []
for obj in processes[process]: for obj in sorted(
if "extra_vars" in obj: [obj for objects in processes.values() for obj in objects], key=get_level
extra_vars |= obj["extra_vars"] ):
if not "options" in obj: if "extra_vars" in obj:
continue 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( convert.parse_root_file(
f'rougail.config.{obj["name"]}', f'rougail.config.{obj["name"]}',
"", "",
"1.1", "1.1",
YAML().load(obj["options"]), YAML().load(option),
) )
tiram_obj = convert.save(None) tiram_obj = convert.save()
optiondescription = {} optiondescription = {}
exec(tiram_obj, {}, optiondescription) # pylint: disable=W0122 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( return _RougailConfig(
backward_compatibility, backward_compatibility,
optiondescription["option_0"], add_extra_options,
extra_vars=extra_vars,
) )

View file

@ -0,0 +1,66 @@
"""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 tiramisu import Config
from warnings import warn
from .convert import RougailConvert
from ..config import RougailConfig
from ..user_datas import UserDatas
from ..tiramisu import tiramisu_display_name
class Rougail(UserDatas):
"""Main Rougail object"""
def __init__(
self,
rougailconfig=None,
) -> None:
if rougailconfig is None:
rougailconfig = RougailConfig
self.rougailconfig = rougailconfig
self.converted = RougailConvert(self.rougailconfig)
self.config = None
def get_root_option(self):
tiram_obj = self.converted.save()
optiondescription = {}
custom_types = {
custom.__name__: custom
for custom in self.rougailconfig["custom_types"].values()
}
exec(tiram_obj, custom_types, optiondescription) # pylint: disable=W0122
return optiondescription["option_0"]
def run(self):
"""Get Tiramisu Config"""
if not self.config:
self.config = Config(
self.get_root_option(),
display_name=tiramisu_display_name,
)
self.config.property.read_write()
return self.config
def get_config(self):
warn(
"get_config is deprecated, use run instead",
DeprecationWarning,
stacklevel=2,
)
return self.run()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

238
src/rougail/convert/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 ..tiramisu 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

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -30,10 +30,10 @@ from typing import Optional, Union
from json import dumps from json import dumps
from os.path import isfile, basename from os.path import isfile, basename
from .i18n import _ from ..i18n import _
from .error import DictConsistencyError, VariableCalculationDependencyError from ..error import DictConsistencyError, VariableCalculationDependencyError
from .utils import normalize_family from ..tiramisu import normalize_family, CONVERT_OPTION
from .object_model import Calculation, CONVERT_OPTION from .object_model import Calculation
class BaseElt: # pylint: disable=R0903 class BaseElt: # pylint: disable=R0903
@ -73,12 +73,7 @@ class TiramisuReflector:
"from tiramisu import *", "from tiramisu import *",
"from tiramisu.setting import ALLOWED_LEADER_PROPERTIES", "from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
"from re import compile as re_compile", "from re import compile as re_compile",
] "from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription",
)
if self.objectspace.export_with_import:
self.text["header"].extend(
[
"from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription"
] ]
) )
if funcs_paths: if funcs_paths:
@ -87,7 +82,7 @@ class TiramisuReflector:
continue continue
self.text["header"].append(f"load_functions('{funcs_path}')") self.text["header"].append(f"load_functions('{funcs_path}')")
if self.objectspace.export_with_import: if self.objectspace.export_with_import:
if objectspace.main_namespace: if self.objectspace.has_namespace:
self.text["header"].extend( self.text["header"].extend(
[ [
"try:", "try:",
@ -131,33 +126,6 @@ class TiramisuReflector:
elt, elt,
self, self,
) )
# else:
# path_prefixes = self.objectspace.paths.get_path_prefixes()
# for path_prefix in path_prefixes:
# space = self.objectspace.space.variables[path_prefix]
# self.set_name(space)
# baseprefix = Family(
# space,
# self,
# )
# basefamily.add(baseprefix)
# for elt in self.reorder_family(space):
# self.populate_family(
# baseprefix,
# elt,
# )
# if not hasattr(baseprefix.elt, "information"):
# baseprefix.elt.information = self.objectspace.information(
# baseprefix.elt.xmlfiles
# )
# for key, value in self.objectspace.paths.get_providers_path(
# path_prefix
# ).items():
# setattr(baseprefix.elt.information, key, value)
# for key, value in self.objectspace.paths.get_suppliers_path(
# path_prefix
# ).items():
# setattr(baseprefix.elt.information, key, value)
baseelt.name = normalize_family(self.objectspace.base_option_name) baseelt.name = normalize_family(self.objectspace.base_option_name)
baseelt.description = self.objectspace.base_option_name baseelt.description = self.objectspace.base_option_name
self.reflector_objects[baseelt.path].get( self.reflector_objects[baseelt.path].get(
@ -253,14 +221,30 @@ class Common:
properties = [] properties = []
calc_properties = [] calc_properties = []
for property_, value in values.items(): for property_, value in values.items():
if value is True: if not isinstance(value, list):
properties.append(self.convert_str(property_)) value = [value]
elif isinstance(value, list): for val in value:
for val in value: ret = self.calculation_property(val)
calc_properties.append(self.calculation_value(val)) if isinstance(ret, bool):
else: if ret:
calc_properties.append(self.calculation_value(value)) properties.append(self.convert_str(property_))
return "frozenset({" + ", ".join(sorted(properties) + calc_properties) + "})" elif ret is not None:
calc_properties.append(ret)
if properties or calc_properties:
return "frozenset({" + ", ".join(sorted(properties) + calc_properties) + "})"
raise Exception('ca existe alors ...')
def calculation_property(
self,
value: Union[Calculation, bool],
) -> Optional[bool]:
if isinstance(value, Calculation):
try:
return self.calculation_value(value)
except VariableCalculationDependencyError:
return None
return value
def calc_properties( def calc_properties(
self, self,
@ -295,40 +279,14 @@ class Common:
): ):
"""Populate variable parameters""" """Populate variable parameters"""
if not isinstance(param, dict): if not isinstance(param, dict):
if isinstance(param, str): param = {
value = self.convert_str(param) "type": "any",
else: "value": param,
value = param }
return f"ParamValue({value})"
if param["type"] == "value": if param["type"] == "value":
return f"ParamValue({param['value']})" return f"ParamValue({param['value']})"
if param["type"] == "information": if param["type"] == "information":
# default? really? return self.build_information_param(param)
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})'
if param["type"] == "identifier": if param["type"] == "identifier":
if "identifier" in param and param["identifier"] != None: if "identifier" in param and param["identifier"] != None:
return f"ParamIdentifier(identifier_index={param['identifier']})" return f"ParamIdentifier(identifier_index={param['identifier']})"
@ -336,13 +294,7 @@ class Common:
if param["type"] == "index": if param["type"] == "index":
return "ParamIndex()" return "ParamIndex()"
if param["type"] == "variable": if param["type"] == "variable":
return self.build_option_param( return self.build_variable_param(param)
param["variable"],
param.get("propertyerror", True),
param.get("identifier"),
param.get("dynamic"),
param.get("whole", False),
)
if param["type"] == "any": if param["type"] == "any":
if isinstance(param["value"], str): if isinstance(param["value"], str):
value = self.convert_str(param["value"]) value = self.convert_str(param["value"])
@ -351,15 +303,40 @@ class Common:
return "ParamValue(" + value + ")" return "ParamValue(" + value + ")"
raise Exception("pfff") 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, self,
variable, param: dict,
propertyerror,
identifier: Optional[str],
dynamic,
whole: bool,
) -> str: ) -> str:
"""build variable parameters""" """build variable parameters"""
variable = param["variable"]
whole = param.get("whole", False)
if variable.path == self.elt.path: if variable.path == self.elt.path:
return f"ParamSelfOption(whole={whole})" return f"ParamSelfOption(whole={whole})"
if whole: if whole:
@ -369,7 +346,13 @@ class Common:
self.calls, self.elt.path self.calls, self.elt.path
) )
params = [f"{option_name}"] params = [f"{option_name}"]
identifier = param.get("identifier")
if identifier is not None: 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" param_type = "ParamDynOption"
identifiers = [] identifiers = []
for ident in identifier: for ident in identifier:
@ -377,9 +360,11 @@ class Common:
ident = self.convert_str(ident) ident = self.convert_str(ident)
identifiers.append(str(ident)) identifiers.append(str(ident))
params.append("[" + ", ".join(identifiers) + "]") params.append("[" + ", ".join(identifiers) + "]")
if param.get("optional", False):
params.append("optional=True")
else: else:
param_type = "ParamOption" param_type = "ParamOption"
if not propertyerror: if not param.get("propertyerror", True):
params.append("notraisepropertyerror=True") params.append("notraisepropertyerror=True")
return f'{param_type}({", ".join(params)})' return f'{param_type}({", ".join(params)})'
@ -389,11 +374,15 @@ class Common:
) -> str: ) -> str:
"""Generate calculated value""" """Generate calculated value"""
child = function.to_function(self.objectspace) child = function.to_function(self.objectspace)
if isinstance(child, str):
return self.convert_str(child)
elif not isinstance(child, dict):
return child
new_args = [] new_args = []
kwargs = [] kwargs = []
if "params" in child: if "params" in child:
for key, value in child["params"].items(): for key, value in child["params"].items():
if not key: if key is None:
for val in value: for val in value:
new_args.append(self.populate_param(val)) new_args.append(self.populate_param(val))
else: else:
@ -418,10 +407,10 @@ class Common:
datas: Union[Calculation, str, list], datas: Union[Calculation, str, list],
return_a_tuple: bool = False, return_a_tuple: bool = False,
) -> str: ) -> str:
if isinstance(datas, str):
return self.convert_str(datas)
if isinstance(datas, Calculation): if isinstance(datas, Calculation):
return self.calculation_value(datas) datas = self.calculation_value(datas)
elif isinstance(datas, str):
datas = self.convert_str(datas)
if not isinstance(datas, list): if not isinstance(datas, list):
return datas return datas
params = [] params = []
@ -430,9 +419,11 @@ class Common:
try: try:
params.append(self.calculation_value(data)) params.append(self.calculation_value(data))
except VariableCalculationDependencyError: except VariableCalculationDependencyError:
pass continue
elif isinstance(data, str): elif isinstance(data, str):
params.append(self.convert_str(data)) params.append(self.convert_str(data))
elif isinstance(data, dict):
params.append(data)
else: else:
params.append(str(data)) params.append(str(data))
if return_a_tuple: if return_a_tuple:
@ -473,9 +464,14 @@ class Variable(Common):
) )
return return
if self.elt.type == "choice": if self.elt.type == "choice":
keys["values"] = self.populate_calculation( try:
self.elt.choices, return_a_tuple=True keys["values"] = self.populate_calculation(
) self.elt.choices, return_a_tuple=True
)
if keys["values"] == '(,)':
keys["values"] = tuple()
except VariableCalculationDependencyError:
keys["values"] = tuple()
if self.elt.type == "regexp": if self.elt.type == "regexp":
self.object_type = "Regexp_" + self.option_name self.object_type = "Regexp_" + self.option_name
self.tiramisu.text["header"].append( self.tiramisu.text["header"].append(
@ -548,7 +544,10 @@ class Family(Common):
if self.group_type: if self.group_type:
keys["group_type"] = self.group_type keys["group_type"] = self.group_type
if self.elt.type == "dynamic": if self.elt.type == "dynamic":
keys["identifiers"] = self.populate_calculation(self.elt.dynamic) try:
keys["identifiers"] = self.populate_calculation(self.elt.dynamic)
except VariableCalculationDependencyError:
keys["identifiers"] = []
children = [] children = []
for path in self.objectspace.parents[self.elt.path]: for path in self.objectspace.parents[self.elt.path]:
children.append(self.objectspace.paths[path]) children.append(self.objectspace.paths[path])

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -26,13 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from .i18n import _ from .i18n import _
from .tiramisu import display_xmlfiles
def display_xmlfiles(xmlfiles: list) -> str:
"""The function format xmlfiles informations to generate errors"""
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + " and " + '"' + xmlfiles[-1] + '"'
class ConfigError(Exception): class ConfigError(Exception):
@ -69,10 +63,6 @@ class DictConsistencyError(Exception):
self.errno = errno self.errno = errno
class UpgradeError(Exception):
"""Error during XML upgrade"""
## ---- generic exceptions ---- ## ---- generic exceptions ----
@ -81,6 +71,14 @@ class NotFoundError(Exception):
pass pass
class ExtentionError(Exception):
pass
class RougailWarning(UserWarning):
pass
## ---- specific exceptions ---- ## ---- specific exceptions ----

View file

@ -8,7 +8,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the

View file

@ -1,765 +0,0 @@
"""Rougail object model
Silique (https://www.silique.fr)
Copyright (C) 2023-2024
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 Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator
from pydantic import (
BaseModel,
StrictBool,
StrictInt,
StrictFloat,
StrictStr,
ConfigDict,
)
from tiramisu import undefined
from .utils import get_jinja_variable_to_param, get_realpath
from .error import DictConsistencyError, VariableCalculationDependencyError
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
def convert_boolean(value: str) -> bool:
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
if isinstance(value, bool):
return value
value = value.lower()
if value == "true":
return True
elif value == "false":
return False
elif value in ["", None]:
return None
raise Exception(f'unknown boolean value "{value}"')
CONVERT_OPTION = {
"string": dict(opttype="StrOption", example="example"),
"number": dict(opttype="IntOption", func=int, example=42),
"float": dict(opttype="FloatOption", func=float, example=1.42),
"boolean": dict(opttype="BoolOption", func=convert_boolean),
"secret": dict(opttype="PasswordOption", example="secrets"),
"mail": dict(opttype="EmailOption", example="user@example.net"),
"unix_filename": dict(opttype="FilenameOption", example="/tmp/myfile.txt"),
"date": dict(opttype="DateOption", example="2000-01-01"),
"unix_user": dict(opttype="UsernameOption", example="username"),
"ip": dict(
opttype="IPOption", initkwargs={"allow_reserved": True}, example="1.1.1.1"
),
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}, example="1.1.1.0/24"),
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
"network": dict(opttype="NetworkOption", example="1.1.1.0"),
"network_cidr": dict(
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24"
),
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
"netbios": dict(
opttype="DomainnameOption",
initkwargs={"type": "netbios", "warnings_only": True},
example="example",
),
"domainname": dict(
opttype="DomainnameOption",
initkwargs={"type": "domainname", "allow_ip": False},
example="example.net",
),
"hostname": dict(
opttype="DomainnameOption",
initkwargs={"type": "hostname", "allow_ip": False},
example="example",
),
"web_address": dict(
opttype="URLOption",
initkwargs={"allow_ip": False, "allow_without_dot": True},
example="https://example.net",
),
"port": dict(
opttype="PortOption", initkwargs={"allow_private": True}, example="111"
),
"mac": dict(opttype="MACOption", example="00:00:00:00:00"),
"unix_permissions": dict(
opttype="PermissionsOption",
initkwargs={"warnings_only": True},
func=int,
example="644",
),
"choice": dict(opttype="ChoiceOption", example="a_choice"),
"regexp": dict(opttype="RegexpOption"),
#
"symlink": dict(opttype="SymLinkOption"),
}
class Param(BaseModel):
key: str
model_config = ConfigDict(extra="forbid")
def __init__(
self,
path,
attribute,
family_is_dynamic,
is_follower,
xmlfiles,
**kwargs,
) -> None:
super().__init__(**kwargs)
class AnyParam(Param):
type: str
value: Union[BASETYPE, List[BASETYPE]]
class VariableParam(Param):
type: str
variable: str
propertyerror: bool = True
whole: bool = False
optional: bool = False
class IdentifierParam(Param):
type: str
identifier: Optional[int] = None
def __init__(
self,
**kwargs,
) -> None:
if not kwargs["family_is_dynamic"]:
msg = f'identifier parameter for "{kwargs["attribute"]}" in "{kwargs["path"]}" cannot be set none dynamic family'
raise DictConsistencyError(msg, 10, kwargs["xmlfiles"])
super().__init__(**kwargs)
class InformationParam(Param):
type: str
information: str
variable: Optional[str] = None
class IndexParam(Param):
type: str
def __init__(
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"]}"'
raise DictConsistencyError(msg, 25, kwargs["xmlfiles"])
super().__init__(**kwargs)
PARAM_TYPES = {
"any": AnyParam,
"variable": VariableParam,
"identifier": IdentifierParam,
"information": InformationParam,
"index": IndexParam,
}
class Calculation(BaseModel):
path_prefix: Optional[str]
path: str
inside_list: bool
version: str
ori_path: Optional[str] = None
default_values: Any = None
namespace: Optional[str]
xmlfiles: List[str]
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 {}
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"]
params[param.pop("key")] = param
return params
class JinjaCalculation(Calculation):
attribute_name: Literal[
"frozen",
"hidden",
"mandatory",
"empty",
"disabled",
"default",
"validators",
"choices",
"dynamic",
]
jinja: StrictStr
params: Optional[List[Param]] = None
return_type: BASETYPE = None
description: Optional[StrictStr] = None
def _jinja_to_function(
self,
function,
return_type,
multi,
objectspace,
*,
add_help=False,
params: Optional[dict] = None,
):
variable = objectspace.paths[self.path]
jinja_path = f"{self.attribute_name}_{self.path}"
idx = 0
while jinja_path in objectspace.jinja:
jinja_path = f"{self.attribute_name}_{self.path}_{idx}"
idx += 1
objectspace.jinja[jinja_path] = self.jinja
default = {
"function": function,
"params": {
"__internal_jinja": jinja_path,
"__internal_type": return_type,
"__internal_multi": multi,
"__internal_files": self.xmlfiles,
"__internal_attribute": self.attribute_name,
"__internal_variable": self.path,
},
}
if self.default_values:
default["params"]["__default_value"] = self.default_values
if add_help:
default["help"] = function + "_help"
if self.params:
default["params"] |= self.get_params(objectspace)
if params:
default["params"] |= params
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
for sub_variable, identifier, true_path in get_jinja_variable_to_param(
path,
self.jinja,
objectspace,
variable.xmlfiles,
objectspace.functions,
self.path_prefix,
self.version,
self.namespace,
):
if true_path in default["params"]:
continue
if isinstance(sub_variable, dict):
default["params"][true_path] = {
"type": "value",
"value": sub_variable,
}
else:
default["params"][true_path] = {
"type": "variable",
"variable": sub_variable,
}
if self.version != "1.0":
default["params"][true_path]["propertyerror"] = False
if identifier:
default["params"][true_path]["identifier"] = identifier
return default
def to_function(
self,
objectspace,
) -> dict:
if self.attribute_name == "default":
if self.return_type:
raise Exception("return_type not allowed!")
variable = objectspace.paths[self.path]
return_type = variable.type
if self.inside_list:
multi = False
elif self.path in objectspace.followers:
multi = objectspace.multis[self.path] == "submulti"
else:
multi = self.path in objectspace.multis
return self._jinja_to_function(
"jinja_to_function",
return_type,
multi,
objectspace,
)
elif self.attribute_name == "validators":
if self.return_type:
raise Exception("pfff")
return self._jinja_to_function(
"valid_with_jinja",
"string",
False,
objectspace,
)
elif self.attribute_name in PROPERTY_ATTRIBUTE:
if self.return_type:
raise Exception("return_type not allowed!")
return self._jinja_to_function(
"jinja_to_property",
"string",
False,
objectspace,
add_help=True,
params={None: [self.attribute_name], "when": True, "inverse": False},
)
elif self.attribute_name == "choices":
return_type = self.return_type
if return_type is None:
return_type = "string"
return self._jinja_to_function(
"jinja_to_function",
return_type,
not self.inside_list,
objectspace,
)
elif self.attribute_name == "dynamic":
return self._jinja_to_function(
"jinja_to_function",
"string",
True,
objectspace,
)
raise Exception("hu?")
class _VariableCalculation(Calculation):
variable: StrictStr
propertyerror: bool = True
allow_none: bool = False
def get_variable(
self,
objectspace,
) -> "Variable":
if self.ori_path is None:
path = self.path
else:
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 and not isinstance(variable, objectspace.variable):
# FIXME remove the pfff
raise Exception("pfff it's a family")
return variable, identifier
def get_params(
self,
objectspace,
variable: "Variable",
identifier: Optional[str],
*,
needs_multi: Optional[bool] = None,
):
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)
param = {
"type": "variable",
"variable": variable,
"propertyerror": self.propertyerror,
}
if identifier:
param["identifier"] = 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"}}
return params
class VariableCalculation(_VariableCalculation):
attribute_name: Literal["default", "choices", "dynamic"]
optional: bool = False
def to_function(
self,
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}"'
raise DictConsistencyError(msg, 33, self.xmlfiles)
variable, identifier = self.get_variable(objectspace)
if not variable and self.optional:
raise VariableCalculationDependencyError()
params = self.get_params(
objectspace,
variable,
identifier,
)
return {
"function": "calc_value",
"params": params,
}
class VariablePropertyCalculation(_VariableCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
when: Any = undefined
when_not: Any = undefined
def to_function(
self,
objectspace,
) -> dict:
variable, identifier = self.get_variable(objectspace)
params = self.get_params(
objectspace,
variable,
identifier,
needs_multi=False,
)
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
else:
if variable.type != "boolean":
raise Exception("only boolean!")
when = True
inverse = False
params[None].insert(0, self.attribute_name)
params["when"] = when
params["inverse"] = inverse
return {
"function": "variable_to_property",
"params": params,
"help": "variable_to_property",
}
class InformationCalculation(Calculation):
attribute_name: Literal["default", "choice", "dynamic"]
information: StrictStr
variable: Optional[StrictStr]
def to_function(
self,
objectspace,
) -> dict:
params = {
None: [
{
"type": "information",
"information": self.information,
}
]
}
if self.variable:
if self.ori_path is None:
path = self.path
else:
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 self.default_values:
params["__default_value"] = self.default_values
return {
"function": "calc_value",
"params": params,
}
class _IdentifierCalculation(Calculation):
identifier: Optional[int] = None
def get_identifier(self) -> dict:
identifier = {"type": "identifier"}
if self.identifier is not None:
identifier["identifier"] = self.identifier
return identifier
class IdentifierCalculation(_IdentifierCalculation):
attribute_name: Literal["default", "choice", "dynamic"]
def to_function(
self,
objectspace,
) -> dict:
identifier = {"type": "identifier"}
if self.identifier is not None:
identifier["identifier"] = self.identifier
return {
"function": "calc_value",
"params": {None: [self.get_identifier()]},
}
class IdentifierPropertyCalculation(_IdentifierCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
when: Any = undefined
when_not: Any = undefined
def to_function(
self,
objectspace,
) -> dict:
if self.version == "1.0":
msg = f'when is not allowed in format version 1.0 for attribute "{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'
raise DictConsistencyError(msg, 35, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
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'
raise DictConsistencyError
params = {
None: [self.attribute_name, self.get_identifier()],
"when": when,
"inverse": inverse,
}
return {
"function": "variable_to_property",
"params": params,
"help": "variable_to_property",
}
class IndexCalculation(Calculation):
attribute_name: Literal["default", "choice", "dynamic"]
def to_function(
self,
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}"'
raise DictConsistencyError(msg, 60, self.xmlfiles)
return {
"function": "calc_value",
"params": {None: [{"type": "index"}]},
}
CALCULATION_TYPES = {
"jinja": JinjaCalculation,
"variable": VariableCalculation,
"information": InformationCalculation,
"identifier": IdentifierCalculation,
"suffix": IdentifierCalculation,
"index": IndexCalculation,
}
CALCULATION_PROPERTY_TYPES = {
"jinja": JinjaCalculation,
"variable": VariablePropertyCalculation,
"information": InformationCalculation,
"identifier": IdentifierPropertyCalculation,
"index": IndexCalculation,
}
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, Calculation, None]
class Family(BaseModel):
name: str
description: Optional[str] = None
type: Literal["family", "leadership", "dynamic"] = "family"
path: str
help: Optional[str] = None
mode: Optional[str] = None
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
namespace: Optional[str]
version: str
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class Dynamic(Family):
# None only for format 1.0
variable: str = None
dynamic: Union[List[Union[StrictStr, Calculation]], Calculation]
class Variable(BaseModel):
# type will be set dynamically in `annotator/value.py`, default is None
type: str = None
name: str
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
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
path: str
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class SymLink(BaseModel):
type: Literal["symlink"] = "symlink"
name: str
path: str
opt: Variable
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid")

View file

@ -1,6 +1,6 @@
""" """
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -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/>. 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 """Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -50,7 +50,6 @@ class Annotator(Walk):
self.not_for_commandline(variable) self.not_for_commandline(variable)
else: else:
self.manage_alternative_name(variable) self.manage_alternative_name(variable)
self.manage_negative_description(variable)
def not_for_commandline(self, variable) -> None: def not_for_commandline(self, variable) -> None:
self.objectspace.properties.add(variable.path, "not_for_commandline", True) self.objectspace.properties.add(variable.path, "not_for_commandline", True)
@ -79,34 +78,11 @@ class Annotator(Walk):
path = alternative_name path = alternative_name
else: else:
path = variable_path.rsplit(".", 1)[0] + "." + alternative_name path = variable_path.rsplit(".", 1)[0] + "." + alternative_name
self.objectspace.version = variable.version
self.objectspace.add_variable( self.objectspace.add_variable(
alternative_name, alternative_name,
{"type": "symlink", "path": path, "opt": variable}, {"type": "symlink", "path": path, "opt": variable},
variable.xmlfiles, variable.xmlfiles,
False, False,
False, False,
variable.version,
)
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":
raise DictConsistencyError(
_(
'negative_description is only available for boolean variable, but "{0}" is "{1}"'
).format(variable.path, variable.type),
201,
variable.xmlfiles,
)
self.objectspace.informations.add(
variable.path, "negative_description", variable.negative_description
) )

View file

@ -2,7 +2,7 @@
Config file for Rougail-structural_commandline Config file for Rougail-structural_commandline
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -27,12 +27,13 @@ def get_rougail_config(
structural_commandline: structural_commandline:
description: Configuration rougail-structural_commandline description: Configuration rougail-structural_commandline
commandline: false commandline: false
add_extra_options: add_extra_options:
description: Add extra options to tiramisu-cmdline-parser description: Add extra options to tiramisu-cmdline-parser
default: true default: true
""" """
return { return {
"name": "exporter", "name": "cmdline",
"process": "structural", "process": "structural",
"options": options, "options": options,
"level": 20, "level": 20,

View file

@ -1,7 +1,7 @@
"""Annotate to add specify attribute for tiramisu-cmdline """Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -24,11 +24,7 @@ from pydantic import BaseModel
class Variable(BaseModel): class Variable(BaseModel):
alternative_name: Optional[str] = None alternative_name: Optional[str] = None
commandline: bool = True commandline: bool = True
negative_description: Optional[str] = None
class Family(BaseModel): class Family(BaseModel):
commandline: bool = True 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 ..tiramisu import normalize_family
from ..convert.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_structural_files_all = rougailconfig["sort_structural_files_all"]
if rougailconfig["main_namespace"]:
self.convert.paths = Paths(rougailconfig["main_namespace"])
self.load_with_extra(
rougailconfig["extra_namespaces"],
rougailconfig["main_namespace"],
rougailconfig["main_structural_directories"],
)
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_structural_directories"]
):
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_structural_files_all:
filenames = {}
for directory_name in directories:
directory = Path(directory_name)
if not self.sort_structural_files_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_structural_files_all:
for filename in sorted(filenames):
yield filenames[filename]
if self.sort_structural_files_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 structural 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,102 @@
"""
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_structural_directories:
description: {_("Directories where structural 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_structural_files_all:
description: {_("Sort structural from differents directories")}
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_namespaces:
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 structural 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

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -27,22 +27,137 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Any 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.machinery import SourceFileLoader as _SourceFileLoader
from importlib.util import ( from importlib.util import (
spec_from_loader as _spec_from_loader, spec_from_loader as _spec_from_loader,
module_from_spec as _module_from_spec, module_from_spec as _module_from_spec,
) )
from unicodedata import normalize, combining
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.object_model import CONVERT_OPTION from re import findall
from rougail.error import display_xmlfiles from tiramisu import DynOptionDescription, calc_value, function_waiting_for_error
from tiramisu import function_waiting_for_error from tiramisu.error import (
from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError ValueWarning,
from .utils import normalize_family ConfigError,
PropertiesOptionError,
CancelParam,
errors,
)
ori_raise_carry_out_calculation_error = errors.raise_carry_out_calculation_error
try:
from .i18n import _
except ModuleNotFoundError:
# FIXME
def _(msg):
return msg
def display_xmlfiles(xmlfiles: list) -> str:
"""The function format xmlfiles informations to generate errors"""
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + " and " + '"' + xmlfiles[-1] + '"'
def convert_boolean(value: str) -> bool:
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
if isinstance(value, bool):
return value
value = value.lower()
if value == "true":
return True
elif value == "false":
return False
elif value in ["", None]:
return None
raise Exception(_('unknown boolean value "{0}"').format(value))
CONVERT_OPTION = {
"string": dict(opttype="StrOption", example="example"),
"number": dict(opttype="IntOption", func=int, example=42),
"integer": dict(opttype="IntOption", func=int, example=42),
"float": dict(opttype="FloatOption", func=float, example=1.42),
"boolean": dict(opttype="BoolOption", func=convert_boolean),
"secret": dict(opttype="PasswordOption", example="secrets"),
"mail": dict(opttype="EmailOption", example="user@example.net"),
"unix_filename": dict(opttype="FilenameOption", example="/tmp/myfile.txt"),
"date": dict(opttype="DateOption", example="2000-01-01"),
"unix_user": dict(opttype="UsernameOption", example="username"),
"ip": dict(
opttype="IPOption", initkwargs={"allow_reserved": True}, example="1.1.1.1"
),
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}, example="1.1.1.0/24"),
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
"network": dict(opttype="NetworkOption", example="1.1.1.0"),
"network_cidr": dict(
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24"
),
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
"netbios": dict(
opttype="DomainnameOption",
initkwargs={"type": "netbios", "warnings_only": True},
example="example",
),
"domainname": dict(
opttype="DomainnameOption",
initkwargs={"type": "domainname", "allow_ip": False},
example="example.net",
),
"hostname": dict(
opttype="DomainnameOption",
initkwargs={"type": "hostname", "allow_ip": False},
example="example",
),
"web_address": dict(
opttype="URLOption",
initkwargs={"allow_ip": False, "allow_without_dot": True},
example="https://example.net",
),
"port": dict(
opttype="PortOption", initkwargs={"allow_private": True}, example="111"
),
"mac": dict(opttype="MACOption", example="00:00:00:00:00"),
"unix_permissions": dict(
opttype="PermissionsOption",
initkwargs={"warnings_only": True},
func=int,
example="644",
),
"choice": dict(opttype="ChoiceOption", example="a_choice"),
"regexp": dict(opttype="RegexpOption"),
#
"symlink": dict(opttype="SymLinkOption"),
}
RENAME_TYPE = {'number': 'integer'}
def get_identifier_from_dynamic_family(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 raise_carry_out_calculation_error(subconfig, *args, **kwargs):
try:
ori_raise_carry_out_calculation_error(subconfig, *args, **kwargs)
except ConfigError as err:
ymlfiles = subconfig.config_bag.context.get_values().get_information(
subconfig, "ymlfiles", []
)
raise ConfigError(_("{0} in {1}").format(err, display_xmlfiles(ymlfiles)))
errors.raise_carry_out_calculation_error = raise_carry_out_calculation_error
global func global func
@ -90,8 +205,10 @@ def test_propertyerror(value: Any) -> bool:
ENV.tests["propertyerror"] = test_propertyerror 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 global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
if dict_func is None:
dict_func = func
loader = _SourceFileLoader("func", path) loader = _SourceFileLoader("func", path)
spec = _spec_from_loader(loader.name, loader) spec = _spec_from_loader(loader.name, loader)
func_ = _module_from_spec(spec) func_ = _module_from_spec(spec)
@ -99,16 +216,78 @@ def load_functions(path):
for function in dir(func_): for function in dir(func_):
if function.startswith("_"): if function.startswith("_"):
continue continue
func[function] = getattr(func_, function) dict_func[function] = getattr(func_, function)
def rougail_calc_value(*args, __default_value=None, **kwargs): def normalize_family(family_name: str) -> str:
"""replace space, accent, uppercase, ... by valid character"""
if not family_name:
return
family_name = family_name.lower()
family_name = family_name.replace("-", "_").replace(" ", "_").replace(".", "_")
nfkd_form = normalize("NFKD", family_name)
family_name = "".join([c for c in nfkd_form if not combining(c)])
return family_name.lower()
def tiramisu_display_name(
kls,
subconfig,
with_quote: bool = False,
) -> str:
"""Replace the Tiramisu display_name function to display path + description"""
config_bag = subconfig.config_bag
context = config_bag.context
values = context.get_values()
context_subconfig = context.get_root(config_bag)
doc = values.get_information(subconfig, "doc", None)
comment = doc if doc and doc != kls.impl_getname() else ""
if "{{ identifier }}" in comment and subconfig.identifiers:
comment = comment.replace("{{ identifier }}", str(subconfig.identifiers[-1]))
path_in_description = values.get_information(
context_subconfig, "path_in_description", True
)
if path_in_description or not comment:
comment = f" ({comment})" if comment else ""
if path_in_description is False:
path = kls.impl_getname()
else:
path = kls.impl_getpath()
if "{{ identifier }}" in path and subconfig.identifiers:
path = path.replace(
"{{ identifier }}", normalize_family(str(subconfig.identifiers[-1]))
)
else:
path = comment
comment = ""
if with_quote:
return f'"{path}"{comment}'
return f"{path}{comment}"
def rougail_calc_value(*args, __default_value=None, __internal_multi=False, **kwargs):
values = calc_value(*args, **kwargs) values = calc_value(*args, **kwargs)
if values is None and __internal_multi:
values = []
if __default_value is not None and values in [None, []]: if __default_value is not None and values in [None, []]:
return __default_value return __default_value
return values 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 @function_waiting_for_error
def jinja_to_function( def jinja_to_function(
__internal_variable, __internal_variable,
@ -128,9 +307,15 @@ def jinja_to_function(
if "." in key: if "." in key:
c_kw = kw c_kw = kw
path, var = key.rsplit(".", 1) 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("."): for subkey in path.split("."):
c_kw = c_kw.setdefault(subkey, {}) c_kw = c_kw.setdefault(subkey, {})
c_kw[var] = value if not isinstance(value, CancelParam):
c_kw[var] = value
else: else:
if key in kw: if key in kw:
raise ConfigError( raise ConfigError(
@ -140,12 +325,23 @@ def jinja_to_function(
try: try:
values = ENV.get_template(__internal_jinja).render(kw, **func).strip() values = ENV.get_template(__internal_jinja).render(kw, **func).strip()
except Exception as err: except Exception as err:
kw_str = ", ".join(kw_to_string(kw))
raise ConfigError( 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 ) from err
convert = CONVERT_OPTION[__internal_type].get("func", str) convert = CONVERT_OPTION[__internal_type].get("func", str)
if __internal_multi: 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: if not values and __default_value is not None:
return __default_value return __default_value
return values return values
@ -161,7 +357,7 @@ def jinja_to_function(
return values return values
def variable_to_property(prop, value, when, inverse): def variable_to_property(prop, value, when, inverse, **kwargs):
if isinstance(value, PropertiesOptionError): if isinstance(value, PropertiesOptionError):
raise value from value raise value from value
if inverse: if inverse:
@ -200,6 +396,7 @@ func["jinja_to_property"] = jinja_to_property
func["jinja_to_property_help"] = jinja_to_property_help func["jinja_to_property_help"] = jinja_to_property_help
func["variable_to_property"] = variable_to_property func["variable_to_property"] = variable_to_property
func["valid_with_jinja"] = valid_with_jinja func["valid_with_jinja"] = valid_with_jinja
func["normalize_family"] = normalize_family
class ConvertDynOptionDescription(DynOptionDescription): class ConvertDynOptionDescription(DynOptionDescription):
@ -236,6 +433,14 @@ class ConvertDynOptionDescription(DynOptionDescription):
if "{{ identifier }}" in display: if "{{ identifier }}" in display:
return display.replace( return display.replace(
"{{ identifier }}", "{{ identifier }}",
self.convert_identifier_to_path(self.get_identifiers(subconfig)[-1]), self.convert_identifier_to_path(self.get_identifiers(subconfig, from_display_name=True)[-1]),
) )
return display return display
def name_could_conflict(self, dynchild, child):
return (
get_identifier_from_dynamic_family(
dynchild.impl_getname(), child.impl_getname()
)
is not None
)

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,424 @@
"""
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 tiramisu import Calculation, owners
from tiramisu.error import (
PropertiesOptionError,
AttributeOptionError,
LeadershipError,
ConfigError,
CancelParam,
)
from .utils import undefined
from .tiramisu import (
normalize_family,
CONVERT_OPTION,
get_identifier_from_dynamic_family,
)
from .error import DictConsistencyError
from .i18n import _
class UserDatas:
def __init__(self, config) -> None:
self.config = config
def user_datas(
self,
user_datas: List[dict],
*,
return_values_not_error=False,
user_datas_type: str = "user_datas",
only_default: bool = False,
):
self.values = {}
self.errors = []
self.warnings = []
self.show_secrets = False
self.only_default = only_default
if user_datas_type == "user_datas":
self._populate_values(user_datas)
else:
self.values = user_datas
self._auto_configure_dynamics()
self._populate_config()
if return_values_not_error:
return self.values
else:
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 = get_identifier_from_dynamic_family(
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 convert_value(self, path, option, options, value):
# converted value
needs_convert = options.get("needs_convert", False)
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)
return value
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
if self.only_default and option.owner.get() != owners.default:
self.values.pop(path)
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.convert_value(
path, option, options, self.values[path]["values"]
)
index = option.index()
if index is not None:
if not isinstance(value, list) or index >= len(value):
continue
value = value[index]
option_without_index = self.config.option(path)
else:
option_without_index = option
if option.isleader():
len_leader = len(option.value.get())
if len_leader:
for idx in range(len_leader - 1, -1, -1):
option.value.pop(idx)
try:
option.value.set(value)
value_is_set = True
except Exception:
if path != option.path():
self.values[option.path()] = self.values.pop(path)
else:
if "source" in self.values[path]:
option_without_index.information.set(
"loaded_from",
_("loaded from {0}").format(self.values[path]["source"]),
)
# 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
for tmp_value in self.values[path]["values"]:
if tmp_value != undefined:
break
else:
self.values.pop(path)
else:
self.values.pop(path)
if not value_is_set:
break
def _display_value(self, option, value):
if not self.show_secrets and option.type() == "password":
return "*" * 10
return value
def _populate_error_warnings(self):
# we don't find variable, apply value just to get error or warning messages
for path, options in self.values.items():
value = options["values"]
option = self.config.option(path)
try:
if option.isoptiondescription():
if value:
self.warnings.append(
_(
'cannot set the value "{0}" to the family {1}, it will be ignored when loading from {2}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
options["source"],
)
)
continue
except AttributeOptionError as err:
if err.code == "option-not-found":
self.warnings.append(
_(
'variable or family "{0}" does not exist, it will be ignored when loading from {1}'
).format(err.path, options["source"])
)
elif err.code == "option-dynamic":
self.warnings.append(
_(
'"{0}" is the name of a dynamic family, it will be ignored when loading from {1}'
).format(option.description(with_quote=True), options["source"])
)
else:
self.warnings.append(
_("{0} loaded from {1}").format(err, options["source"])
)
continue
value = self.convert_value(
path, option, self.values[path].get("options", {}), value
)
if option.isfollower():
indexes = range(len(value))
values = value
else:
indexes = [None]
for index in indexes:
try:
if option.isfollower():
value = values[index]
if value is undefined or isinstance(value, CancelParam):
continue
self.config.option(path, index).value.set(value)
else:
option.value.set(value)
except PropertiesOptionError as err:
if err.code == "property-error":
properties = err.display_properties(
force_property=True, add_quote=False
)
err_path = err._subconfig.path
display_name = option.description(with_quote=True)
if index is not None:
if path == err_path:
self.warnings.append(
_(
'variable {0} at index "{1}" is {2}, it will be ignored when loading from {3}'
).format(
display_name,
index,
properties,
options["source"],
)
)
else:
self.warnings.append(
_(
'family {0} is {1}, {2} at index "{3}" will be ignored when loading from {4}'
).format(
err._name,
properties,
display_name,
index,
options["source"],
)
)
else:
if path == err_path:
self.warnings.append(
_(
"variable {0} is {1}, it will be ignored when loading from {2}"
).format(
display_name, properties, options["source"]
)
)
else:
self.warnings.append(
_(
"family {0} is {1}, {2} will be ignored when loading from {3}"
).format(
err._name,
properties,
display_name,
options["source"],
)
)
else:
self.warnings.append(
_("{0} in {1}").format(err, options["source"])
)
except LeadershipError as err:
self.warnings.append(_("{0} in {1}").format(err, options["source"]))
except ValueError as err:
err.prefix = ""
if index is not None:
self.warnings.append(
_(
'the value "{0}" is invalid for {1} at index "{2}", {3}, it will be ignored when loading from {4}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
index,
err,
options["source"],
)
)
else:
self.warnings.append(
_(
'the value "{0}" is invalid for {1}, {2}, it will be ignored when loading from {3}'
).format(
self._display_value(option, value),
option.description(with_quote=True),
err,
options["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:
try:
return func(value)
except:
pass
return value

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) 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 This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the under the terms of the GNU Lesser General Public License as published by the
@ -26,7 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from typing import List, Union from typing import List, Union
from unicodedata import normalize, combining
import re import re
from itertools import chain from itertools import chain
@ -45,6 +44,8 @@ from .error import DictConsistencyError
NAME_REGEXP = re.compile(r"^[a-z0-9_]*$") NAME_REGEXP = re.compile(r"^[a-z0-9_]*$")
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
def valid_variable_family_name( def valid_variable_family_name(
name: str, name: str,
@ -58,17 +59,6 @@ def valid_variable_family_name(
raise DictConsistencyError(msg, 76, xmlfiles) raise DictConsistencyError(msg, 76, xmlfiles)
def normalize_family(family_name: str) -> str:
"""replace space, accent, uppercase, ... by valid character"""
if not family_name:
return
family_name = family_name.lower()
family_name = family_name.replace("-", "_").replace(" ", "_").replace(".", "_")
nfkd_form = normalize("NFKD", family_name)
family_name = "".join([c for c in nfkd_form if not combining(c)])
return family_name.lower()
def load_modules(name, module) -> List[str]: def load_modules(name, module) -> List[str]:
"""list all functions in a module""" """list all functions in a module"""
loader = SourceFileLoader(name, module) loader = SourceFileLoader(name, module)
@ -78,13 +68,10 @@ def load_modules(name, module) -> List[str]:
return eosfunc return eosfunc
def get_realpath( # def get_realpath(
path: str, # path: str,
path_prefix: str, # ) -> str:
) -> str: # return path
if path_prefix:
return f"{path_prefix}.{path}"
return path
def get_jinja_variable_to_param( def get_jinja_variable_to_param(
@ -93,7 +80,6 @@ def get_jinja_variable_to_param(
objectspace, objectspace,
xmlfiles, xmlfiles,
functions, functions,
path_prefix,
version, version,
namespace, namespace,
): ):
@ -118,6 +104,8 @@ def get_jinja_variable_to_param(
jinja_text, current_path, err jinja_text, current_path, err
) )
raise DictConsistencyError(msg, 39, xmlfiles) from err raise DictConsistencyError(msg, 39, xmlfiles) from err
except AttributeError:
pass
variables = list(variables) variables = list(variables)
variables.sort(reverse=True) variables.sort(reverse=True)
founded_variables = {} founded_variables = {}
@ -125,7 +113,6 @@ def get_jinja_variable_to_param(
for variable_path in variables: for variable_path in variables:
variable, identifier = objectspace.paths.get_with_dynamic( variable, identifier = objectspace.paths.get_with_dynamic(
variable_path, variable_path,
path_prefix,
current_path, current_path,
version, version,
namespace, namespace,
@ -152,7 +139,6 @@ def get_jinja_variable_to_param(
vpath = vpath.rsplit(".", 1)[0] vpath = vpath.rsplit(".", 1)[0]
variable, identifier = objectspace.paths.get_with_dynamic( variable, identifier = objectspace.paths.get_with_dynamic(
vpath, vpath,
path_prefix,
current_path, current_path,
version, version,
namespace, namespace,
@ -165,3 +151,60 @@ def get_jinja_variable_to_param(
yield {}, None, root_path yield {}, None, root_path
for variable_path, data in founded_variables.items(): for variable_path, data in founded_variables.items():
yield data[1], data[0], variable_path 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
class Undefined:
pass
undefined = Undefined()

View file

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

View file

@ -0,0 +1 @@
{}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1 @@
{}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -10,6 +10,5 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[], properties=frozenset({"advanced"}), informations={'ymlfiles': ['']})
optiondescription_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])

View file

@ -0,0 +1,9 @@
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_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -10,6 +10,5 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[], properties=frozenset({"advanced"}), informations={'ymlfiles': ['']})
optiondescription_4 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -0,0 +1,9 @@
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_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -10,6 +10,5 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[], properties=frozenset({"advanced"}), informations={'ymlfiles': ['']})
optiondescription_4 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -0,0 +1,9 @@
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_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.version": null
}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -10,6 +10,6 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) option_2 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_0version_underscore/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2], properties=frozenset({"basic"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

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,9 +2,9 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) option_1 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_0version_underscore/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.empty": null
}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -10,6 +10,6 @@ except:
ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_2 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) option_2 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_1empty_variable/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2], properties=frozenset({"basic"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

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,9 +2,9 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
option_1 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) option_1 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_1empty_variable/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

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

@ -0,0 +1,6 @@
{
"rougail.var1": "no",
"rougail.var2": [
"no"
]
}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -11,7 +11,7 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_rougail.var2'] = "{{ _.var1 }}\n" 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_2 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml'], '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={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) 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 tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_var2'] = "{{ _.var1 }}\n" 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_1 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml'], '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={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2]) 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

@ -0,0 +1,12 @@
{
"rougail.var1": [
"no",
"yes",
"maybe"
],
"rougail.var2": [
"no",
"yes",
"maybe"
]
}

View file

@ -2,7 +2,7 @@ from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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: try:
groups.namespace groups.namespace
except: except:
@ -11,7 +11,7 @@ ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n" 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_2 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml'], '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={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"})) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) 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 tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile from re import compile as re_compile
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription 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("basic")
ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("standard")
ALLOWED_LEADER_PROPERTIES.add("advanced") ALLOWED_LEADER_PROPERTIES.add("advanced")
dict_env['default_var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n" 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_1 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml'], '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={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_multi/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2]) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -0,0 +1,20 @@
{
"rougail.leadership.var1": {
"owner": "default",
"value": [
"a_value"
]
},
"rougail.leadership.var2": {
"owner": [
"default"
],
"value": [
"a_value"
]
},
"rougail.var2": {
"owner": "default",
"value": "a_value"
}
}

View file

@ -0,0 +1,9 @@
{
"rougail.leadership.var1": [
{
"rougail.leadership.var1": "a_value",
"rougail.leadership.var2": "a_value"
}
],
"rougail.var2": "a_value"
}

View file

@ -0,0 +1,20 @@
{
"rougail.leadership.var1": {
"owner": "default",
"value": [
"a_value"
]
},
"rougail.leadership.var2": {
"owner": [
"default"
],
"value": [
"a_value"
]
},
"rougail.var2": {
"owner": "default",
"value": "a_value"
}
}

View file

@ -0,0 +1,3 @@
{
"rougail.var2": "a_value"
}

View file

@ -0,0 +1,19 @@
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")
dict_env['default_rougail.var2'] = "{{ var1[0] }}"
option_3 = StrOption(name="var1", doc="a first variable", multi=True, default=["a_value"], properties=frozenset({"force_default_on_freeze", "frozen", "mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
option_4 = StrOption(name="var2", doc="a first variable", multi=True, default_multi="a_value", properties=frozenset({"force_default_on_freeze", "frozen", "mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
optiondescription_2 = Leadership(name="leadership", doc="leadership", children=[option_3, option_4], properties=frozenset({"hidden", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml']})
option_5 = StrOption(name="var2", doc="a second variable", default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(False), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), 'var1': ParamOption(option_3)})), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[optiondescription_2, option_5], properties=frozenset({"standard"}), informations={'ymlfiles': ['']})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,14 @@
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")
dict_env['default_var2'] = "{{ var1[0] }}"
option_2 = StrOption(name="var1", doc="a first variable", multi=True, default=["a_value"], properties=frozenset({"force_default_on_freeze", "frozen", "mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
option_3 = StrOption(name="var2", doc="a first variable", multi=True, default_multi="a_value", properties=frozenset({"force_default_on_freeze", "frozen", "mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = Leadership(name="leadership", doc="leadership", children=[option_2, option_3], properties=frozenset({"hidden", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml']})
option_4 = StrOption(name="var2", doc="a second variable", default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(False), '__internal_files': ParamValue(['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), 'var1': ParamOption(option_2)})), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_params_permissive/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, option_4])

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,4 @@
{
"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 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable/rougail/00-base.yml'], '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=False, properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable/rougail/00-base.yml'], 'type': 'domainname'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
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 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable/rougail/00-base.yml'], '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=False, properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable/rougail/00-base.yml'], 'type': 'domainname'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

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,4 @@
{
"rougail.var1": null,
"rougail.var2": null
}

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 = StrOption(name="var1", doc="a first variable", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable_description/rougail/00-base.yml'], 'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", default=Calculation(func['calc_value'], Params((ParamOption(option_2)))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable_description/rougail/00-base.yml'], 'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"basic"}), informations={'ymlfiles': ['']})
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 = StrOption(name="var1", doc="a first variable", properties=frozenset({"basic", "mandatory"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable_description/rougail/00-base.yml'], 'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", default=Calculation(func['calc_value'], Params((ParamOption(option_1)))), properties=frozenset({"mandatory", "standard"}), informations={'ymlfiles': ['../rougail-tests/structures/00_2default_calculated_variable_description/rougail/00-base.yml'], 'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

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

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