Compare commits

..

No commits in common. "0.2.0a35" and "main" have entirely different histories.

7457 changed files with 26108 additions and 137196 deletions

View file

@ -1,287 +1,3 @@
## 0.2.0a35 (2025-10-27)
### Feat
- tags documentation
### Fix
- add changelog support for github/gitlab format
- tests
## 0.2.0a34 (2025-10-22)
### Feat
- ca force true_color terminal when export to console
### Fix
- parse subtree for a dynamic
- adapt api for rougail-web-ui
- undocumented variable in dynamic
## 0.2.0a33 (2025-10-16)
### Feat
- can document a variable for a specified identifier
- can remove header when generate a table
- add html output
- changelog for variables
### Fix
- support rougail-web-ui
- black
- remove extra '*'
- always use name for informations key (sometime path was used)
- update tests
- translation for property is now in rougail
## 0.2.0a32 (2025-10-03)
### Fix
- property calculated with disabled variable
- better disabled support
## 0.2.0a31 (2025-10-02)
### Feat
- limit warning when asked
## 0.2.0a30 (2025-10-02)
### Feat
- better support variable calculation for property
## 0.2.0a29 (2025-10-02)
### Fix
- property for namespace
## 0.2.0a28 (2025-10-02)
### Feat
- add gitlab plugin
### Fix
- tests for formatter
- update translation
## 0.2.0a27 (2025-09-29)
### Fix
- doc network with CIDR format
- better doc for calculation with unknown variable
## 0.2.0a26 (2025-09-29)
### Feat
- default value for a calculated variable with an unknown optional variable
- add integer type which will replace number type
## 0.2.0a25 (2025-09-22)
### Feat
- adapt to tiramisu-web-ui
## 0.2.0a24 (2025-06-19)
### Feat
- could not change default value during annotator
## 0.2.0a23 (2025-06-18)
### Fix
- separation
## 0.2.0a22 (2025-06-18)
### Fix
- rougail separation
## 0.2.0a21 (2025-05-12)
### Fix
- update translation
- black
- doc example with leader example lower than leader default value
## 0.2.0a20 (2025-05-09)
### Fix
- undefined is a rougail object
## 0.2.0a19 (2025-05-05)
### Fix
- update translation
## 0.2.0a18 (2025-05-05)
### Fix
- doc default value with undocumented variable
## 0.2.0a17 (2025-05-05)
### Fix
- description for all calculations
- better documentation variable with variable in default attribut
## 0.2.0a16 (2025-04-30)
### Fix
- update translation
- remove negative_description support
- better documentation with hidden variable in property calculation
- use new information ymlfiles
- update tests
- better dynamic support
## 0.2.0a15 (2025-04-09)
### Fix
- version
## 0.2.0a14 (2025-04-07)
### Fix
- calculation for dynamic ans leadership variables
## 0.2.0a13 (2025-04-02)
### Fix
- error in disabled dynamic variable
## 0.2.0a12 (2025-04-01)
### Fix
- update tests
- groups.namespace could be unexistant
## 0.2.0a11 (2025-03-31)
### Fix
- doc a param with a set but with only one item
## 0.2.0a10 (2025-03-30)
### Feat
- document unix file name parameters
- better console output
- do not document reference to undocumented variable
### Fix
- doc for param
- support NamespaceCalculation
- convert <ENV> to &lt;ENV&gt; for github plugin
## 0.2.0a9 (2025-02-19)
### Fix
- support suffix (in version 1.0 format) in calculation
## 0.2.0a8 (2025-02-19)
### Fix
- key is the path
## 0.2.0a7 (2025-02-19)
### Fix
- with_family => without_family and with_example => example
## 0.2.0a6 (2025-02-17)
### Feat
- add with_family parameter
## 0.2.0a5 (2025-02-17)
### Fix
- do not add multiple attribute several time in json export
## 0.2.0a4 (2025-02-10)
### Feat
- output return status too
## 0.2.0a3 (2025-01-04)
### Fix
- detect_symlink => only_self
## 0.2.0a2 (2025-01-04)
### Feat
- create tests
### Fix
- do not document symlink
- remove prefix_path
## 0.2.0a1 (2024-11-28)
### Fix
- separation between run and print function
## 0.2.0a0 (2024-11-20)
### Feat
- personalize mode that we want disable documentation
- add console output
- better dynamique family support
- add json output
### Fix
- correction for properties in italic
## 0.1.1a0 (2024-11-08)
### Fix
- generate documentation with force_optional configuration
## 0.1.0 (2024-11-06)
## 0.1.0rc1 (2024-11-06) ## 0.1.0rc1 (2024-11-06)
### Fix ### Fix

Binary file not shown.

View file

@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2025-10-26 15:19+0100\n" "POT-Creation-Date: 2024-11-01 11:42+0100\n"
"PO-Revision-Date: 2025-10-26 15:20+0100\n" "PO-Revision-Date: 2024-11-01 11:42+0100\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n" "Language: fr\n"
@ -14,364 +14,138 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.7\n" "X-Generator: Poedit 3.5\n"
#: src/rougail/output_doc/annotator.py:324 #: src/rougail/output_doc/__init__.py:40
msgid "the value of the information \"{0}\" of the variable \"{1}\""
msgstr "la valeur de l'information \"{0}\" de la variable \"{1}\""
#: src/rougail/output_doc/annotator.py:327
msgid "the value of the global information \"{0}\""
msgstr "la valeur de l'information globale \"{0}\""
#: src/rougail/output_doc/annotator.py:334
msgid "when the identifier is \"{0}\""
msgstr "lorsque l'identifiant est \"{0}\""
#: src/rougail/output_doc/annotator.py:336
msgid "when the identifier is not \"{0}\""
msgstr "lorsque l'identifiant n'est pas \"{0}\""
#: src/rougail/output_doc/changelog.py:137
msgid "New variable"
msgstr "Nouvelle variable"
#: src/rougail/output_doc/changelog.py:139
msgid "New variables"
msgstr "Nouvelles variables"
#: src/rougail/output_doc/changelog.py:150
msgid "Modified variable"
msgstr "Variable modifiée"
#: src/rougail/output_doc/changelog.py:152
msgid "Modified variables"
msgstr "Variables modifiées"
#: src/rougail/output_doc/changelog.py:163
msgid "Deleted variable"
msgstr "Variable supprimée"
#: src/rougail/output_doc/changelog.py:165
msgid "Deleted variables"
msgstr "Variables supprimées"
#: src/rougail/output_doc/config.py:43
msgid "duplicated level rougail-doc for output \"{0}\": {1} and {2}"
msgstr "niveau dupliqué pour rougail-doc pour la sortie \"{0}\": {1} et {2}"
#: src/rougail/output_doc/config.py:75
msgid "Configuration rougail-doc"
msgstr "Configuration de rougail-doc"
#: src/rougail/output_doc/config.py:83
msgid "Starting title level"
msgstr "Niveau de titre de départ"
#: src/rougail/output_doc/config.py:88
msgid "Generated content"
msgstr "Contenu générer"
#: src/rougail/output_doc/config.py:97
msgid "Previous description file in JSON format"
msgstr "Précédent fichier de description au format JSON"
#: src/rougail/output_doc/config.py:105
msgid "Do not add families in documentation"
msgstr "Ne pas ajouter les familles dans la documentation"
#: src/rougail/output_doc/config.py:114
msgid "Disable documentation for variables with those modes"
msgstr "Désactiver la documentation des variables avec ces modes"
#: src/rougail/output_doc/config.py:122
msgid "disabled when there is no mode available"
msgstr "désactiver lorsqu'il n'y a pas de mode valable"
#: src/rougail/output_doc/config.py:128
msgid "verify if disable modes already exists"
msgstr "vérifier le mode existe déjà"
#: src/rougail/output_doc/config.py:130
msgid "Modify values to document leaderships and dynamics families"
msgstr "Valeurs modifiées pour documenter les familles leader ou dynamique"
#: src/rougail/output_doc/config.py:133
msgid "Generate document in format"
msgstr "Générer le document au format"
#: src/rougail/output_doc/config.py:151
msgid "Force true color terminal"
msgstr "Force les vrais couleurs dans le terminal"
#: src/rougail/output_doc/doc.py:344
msgid "This family contains lists of variable blocks."
msgstr "Cette famille contient des listes de bloc de variable."
#: src/rougail/output_doc/doc.py:354
msgid "This family builds families dynamically."
msgstr "Cette famille construit des familles dynamiquement."
#: src/rougail/output_doc/doc.py:368
msgid "Default"
msgstr "Défaut"
#: src/rougail/output_doc/doc.py:383
msgid "multiple"
msgstr "multiple"
#: src/rougail/output_doc/doc.py:391
msgid "Example"
msgstr "Exemple"
#: src/rougail/output_doc/doc.py:394
msgid "Examples"
msgstr "Exemples"
#: src/rougail/output_doc/doc.py:403
msgid "Tag"
msgstr "Étiquette"
#: src/rougail/output_doc/doc.py:406
msgid "Tags"
msgstr "Étiquettes"
#: src/rougail/output_doc/doc.py:435
msgid "No attribute \"description\" for \"{0}\" in {1}"
msgstr "Aucun attribut \"description\" pour \"{0}\" dans {1}"
#: src/rougail/output_doc/doc.py:577
msgid "text based with regular expressions \"{0}\""
msgstr "texte avec expression rationnelle \"{0}\""
#: src/rougail/output_doc/doc.py:581
msgid "Validator"
msgstr "Validateur"
#: src/rougail/output_doc/doc.py:584
msgid "Validators"
msgstr "Validateurs"
#: src/rougail/output_doc/doc.py:593
msgid "Choices"
msgstr "Choix"
#: src/rougail/output_doc/doc.py:696
msgid "the value of the identifier"
msgstr "la valeur de l'identifiant"
#: src/rougail/output_doc/doc.py:700
msgid "the value of the {0}"
msgstr "la valeur de l'{0}"
#: src/rougail/output_doc/doc.py:709
msgid "depends on a calculation"
msgstr "dépend d'un calcul"
#: src/rougail/output_doc/doc.py:715
msgid "\"{0}\" is a calculation for {1} but has no description in {2}"
msgstr "\"{0}\" est un calcul pour {1} mais n'a pas de description dans {2}"
#: src/rougail/output_doc/doc.py:742 src/rougail/output_doc/doc.py:858
msgid "depends on an undocumented variable"
msgstr "dépends d'une variable non documentée"
#: src/rougail/output_doc/doc.py:758
msgid "when the variable \"{0}\" hasn't the value \"{1}\""
msgstr "lorsque la variable \"{0}\" n'a pas la valeur \"{1}\""
#: src/rougail/output_doc/doc.py:760
msgid "when the variable \"{0}\" is defined and hasn't the value \"{1}\""
msgstr "lorsque la variable \"{0}\" est définie et n'a pas la valeur \"{1}\""
#: src/rougail/output_doc/doc.py:765
msgid "when the variable \"{0}\" has the value \"{1}\""
msgstr "lorsque la variable \"{0}\" a la valeur \"{1}\""
#: src/rougail/output_doc/doc.py:767
msgid "when the variable \"{0}\" is defined and has the value \"{1}\""
msgstr "lorsque la variable \"{0}\" est définie et a la valeur \"{1}\""
#: src/rougail/output_doc/doc.py:785
msgid "the value of the variable \"{0}\""
msgstr "la valeur de la variable \"{0}\""
#: src/rougail/output_doc/doc.py:787
msgid "the value of the variable \"{0}\" if it is defined"
msgstr "la valeur de la variable \"{0}\" si elle est définie"
#: src/rougail/output_doc/doc.py:788
msgid "the value of an undocumented variable"
msgstr "la valeur d'une variable non documentée"
#: src/rougail/output_doc/doc.py:826
msgid "the values of undocumented variables"
msgstr "les valeurs de variables non documentées"
#: src/rougail/output_doc/doc.py:846
msgid "(from an undocumented variable)"
msgstr "(issue d'une variable non documentée)"
#: src/rougail/output_doc/doc.py:854
msgid "{0} (from an undocumented variable)"
msgstr "{0} (issue d'une variable non documentée)"
#: src/rougail/output_doc/example.py:37
msgid "Example with mandatory variables not filled in"
msgstr "Exemple avec des variables obligatoire non renseignée"
#: src/rougail/output_doc/example.py:42
msgid "Example with all variables modifiable"
msgstr "Exemple avec toutes les variables modifiable"
#: src/rougail/output_doc/output/console.py:164
#: src/rougail/output_doc/utils.py:782
msgid "Variable"
msgstr "Variable"
#: src/rougail/output_doc/output/console.py:165
#: src/rougail/output_doc/utils.py:782
msgid "Description"
msgstr "Description"
#: src/rougail/output_doc/output/gitlab.py:51
msgid "Informations"
msgstr "Informations"
#: src/rougail/output_doc/utils.py:44
msgid "the domain name can starts by a dot" msgid "the domain name can starts by a dot"
msgstr "le nom de domaine peut commencer par un point" msgstr "le nom de domaine peut commencé par un point"
#: src/rougail/output_doc/utils.py:45 #: src/rougail/output_doc/__init__.py:41
msgid "the domain name can be a hostname" msgid "the domain name can be a hostname"
msgstr "le nom de domaine peut être un nom d'hôte" msgstr "le nom de domaine peut être un nom d'hôte"
#: src/rougail/output_doc/utils.py:46 #: src/rougail/output_doc/__init__.py:42
msgid "the domain name can be an IP" msgid "the domain name can be an IP"
msgstr "le nom de domaine peut être une IP" msgstr "le nom de domaine peut être une IP"
#: src/rougail/output_doc/utils.py:47 #: src/rougail/output_doc/__init__.py:43
msgid "the domain name can be network in CIDR format" msgid "the domain name can be network in CIDR format"
msgstr "le nom de domaine peut être un réseau au format CIDR" msgstr "le nom de domaine peut être un réseau au format CIDR"
#: src/rougail/output_doc/utils.py:52 src/rougail/output_doc/utils.py:58 #: src/rougail/output_doc/__init__.py:48
msgid "the minimum value is {0}" msgid "the minimum value is {0}"
msgstr "le valeur minimal est {0}" msgstr "le valeur minimal est {0}"
#: src/rougail/output_doc/utils.py:53 src/rougail/output_doc/utils.py:59 #: src/rougail/output_doc/__init__.py:49
msgid "the maximum value is {0}" msgid "the maximum value is {0}"
msgstr "le valeur maximal est {0}" msgstr "le valeur maximal est {0}"
#: src/rougail/output_doc/utils.py:65 #: src/rougail/output_doc/__init__.py:55
msgid "IP must be in CIDR format" msgid "IP must be in CIDR format"
msgstr "IP doit être au format CIDR" msgstr "IP doit être au format CIDR"
#: src/rougail/output_doc/utils.py:66 #: src/rougail/output_doc/__init__.py:56
msgid "private IP are allowed" msgid "private IP are allowed"
msgstr "les IP privées sont autorisés" msgstr "les IP privées sont autorisés"
#: src/rougail/output_doc/utils.py:67 #: src/rougail/output_doc/__init__.py:57
msgid "reserved IP are allowed" msgid "reserved IP are allowed"
msgstr "les IP réservés sont autorisés" msgstr "les IP réservés sont autorisés"
#: src/rougail/output_doc/utils.py:72 #: src/rougail/output_doc/__init__.py:62
msgid "network must be in CIDR format"
msgstr "réseau doit être au format CIDR"
#: src/rougail/output_doc/utils.py:77
msgid "the host name can be an IP" msgid "the host name can be an IP"
msgstr "le nom d'hôte peut être une IP" msgstr "le nom d'hôte peut être une IP"
#: src/rougail/output_doc/utils.py:82 #: src/rougail/output_doc/__init__.py:67
msgid "the domain name in web address can be an IP" msgid "the domain name in web address can be an IP"
msgstr "le nom de domaine dans l'adresse web peut être une IP" msgstr "le nom de domaine dans l'adresse web peut être une IP"
#: src/rougail/output_doc/utils.py:83 #: src/rougail/output_doc/__init__.py:68
msgid "the domain name in web address can be only a hostname" msgid "the domain name in web address can be only a hostname"
msgstr "le nom de domaine dans l'adresse web ne peut être qu'un nom d'hôte" msgstr "le nom de domaine dans l'adresse web ne peut être qu'un nom d'hôte"
#: src/rougail/output_doc/utils.py:90 #: src/rougail/output_doc/__init__.py:75
msgid "can be range of port" msgid "can be range of port"
msgstr "peut être un range de port" msgstr "peut être un range de port"
#: src/rougail/output_doc/utils.py:91 #: src/rougail/output_doc/__init__.py:76
msgid "can have the protocol" msgid "can have the protocol"
msgstr "peut avoir un protocole" msgstr "peut avoir un protocole"
#: src/rougail/output_doc/utils.py:92 #: src/rougail/output_doc/__init__.py:77
msgid "port 0 is allowed" msgid "port 0 is allowed"
msgstr "le port 0 est autorisé" msgstr "le port 0 est autorisé"
#: src/rougail/output_doc/utils.py:93 #: src/rougail/output_doc/__init__.py:78
msgid "well-known ports (1 to 1023) are allowed" msgid "ports 1 to 1023 are allowed"
msgstr "les ports connus (de 1 à 1023) sont autorisés" msgstr "les ports entre 1 et 1023 sont autorisés"
#: src/rougail/output_doc/utils.py:94 #: src/rougail/output_doc/__init__.py:79
msgid "registred ports (1024 to 49151) are allowed" msgid "ports 1024 to 49151 are allowed"
msgstr "les ports enregistrés (de 1024 à 49151) sont autorisés" msgstr "les ports entre 1024 et 49151 sont autorisés"
#: src/rougail/output_doc/utils.py:95 #: src/rougail/output_doc/__init__.py:80
msgid "private ports (greater than 49152) are allowed" msgid "ports greater than 49152 are allowed"
msgstr "les ports privés (supérieurs à 49152) sont autorisés" msgstr "les ports supérieurs à 49152 sont autorisés"
#: src/rougail/output_doc/utils.py:100 #: src/rougail/output_doc/__init__.py:125
msgid "minimum length for the secret is {0} characters" msgid "mandatory"
msgstr "longueur minimum pour le secret est de {0} caractères" msgstr "obligatoire"
#: src/rougail/output_doc/utils.py:101 #: src/rougail/output_doc/__init__.py:126
msgid "maximum length for the secret is {0} characters" msgid "hidden"
msgstr "longueur maximal pour le secret est de {0} caractères" msgstr "caché"
#: src/rougail/output_doc/utils.py:102 #: src/rougail/output_doc/__init__.py:127
msgid "forbidden characters: {0}" msgid "disabled"
msgstr "caractères interdits: {0}" msgstr "désactivé"
#: src/rougail/output_doc/utils.py:107 #: src/rougail/output_doc/__init__.py:128
msgid "this filename could be a relative path" msgid "unique"
msgstr "ce nom de fichier peut être un chemin relative" msgstr "unique"
#: src/rougail/output_doc/utils.py:108 #: src/rougail/output_doc/__init__.py:129
msgid "this file must exists" msgid "auto modified"
msgstr "ce fichier doit exister" msgstr "auto modifiée"
#: src/rougail/output_doc/utils.py:109 #: src/rougail/output_doc/__init__.py:163
msgid "file type allowed: {0}"
msgstr "type de fichier autorisé : {0}"
#: src/rougail/output_doc/utils.py:332
msgid "Variables for \"{0}\"" msgid "Variables for \"{0}\""
msgstr "Variables pour \"{0}\"" msgstr "Variables pour \"{0}\""
#: src/rougail/output_doc/utils.py:362 #: src/rougail/output_doc/__init__.py:180
msgid "Identifiers" msgid "Variables"
msgstr "Identifiants" msgstr "Variables"
#: src/rougail/output_doc/utils.py:659 src/rougail/output_doc/utils.py:668 #: src/rougail/output_doc/__init__.py:187
#: src/rougail/output_doc/utils.py:674 src/rougail/output_doc/utils.py:680 msgid "Example with mandatory variables not filled in"
#: src/rougail/output_doc/utils.py:684 msgstr "Exemple avec des variables obligatoire non renseignée"
#: src/rougail/output_doc/__init__.py:493
msgid "(default)" msgid "(default)"
msgstr "(défaut)" msgstr "(defaut)"
#: src/rougail/output_doc/utils.py:832 #: src/rougail/output_doc/__init__.py:498
msgid "{0}: {1}" msgid "Choices"
msgstr "{0} : {1}" msgstr "Choix"
#~ msgid "Generate example" #: src/rougail/output_doc/__init__.py:501
#~ msgstr "Génération de l'exemple" msgid "Default"
msgstr "Défaut"
#~ msgid "Add families in documentation" #: src/rougail/output_doc/__init__.py:559
#~ msgstr "Ajouter les familles dans la documentation" msgid "the value of the variable \"{0}\""
msgstr "la valeur de la variable \"{0}\""
#~ msgid "Display example in documentation" #: src/rougail/output_doc/__init__.py:564
#~ msgstr "Afficher un exemple dans la documentation" #: src/rougail/output_doc/__init__.py:566
msgid "value of the {0}"
msgstr "la valeur de {0}"
#~ msgid "Hide example in documentation" #: src/rougail/output_doc/__init__.py:638
#~ msgstr "Cacher l'exemple dans la documentation" #: src/rougail/output_doc/__init__.py:651
msgid "Example"
msgstr "Exemple"
#, fuzzy #: src/rougail/output_doc/__init__.py:649
#~| msgid ":" msgid "Examples"
#~ msgid ": " msgstr "Exemples"
#~ msgstr " : "
#~ msgid "when the variable \"{0}\" has the value \"True\""
#~ msgstr "lorsque la variable \"{0}\" a la valeur \"True\""

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: 2025-10-26 15:23+0100\n" "POT-Creation-Date: 2024-11-01 11:42+0100\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,343 +15,137 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: src/rougail/output_doc/annotator.py:324 #: src/rougail/output_doc/__init__.py:40
msgid "the value of the information \"{0}\" of the variable \"{1}\""
msgstr ""
#: src/rougail/output_doc/annotator.py:327
msgid "the value of the global information \"{0}\""
msgstr ""
#: src/rougail/output_doc/annotator.py:334
msgid "when the identifier is \"{0}\""
msgstr ""
#: src/rougail/output_doc/annotator.py:336
msgid "when the identifier is not \"{0}\""
msgstr ""
#: src/rougail/output_doc/changelog.py:137
msgid "New variable"
msgstr ""
#: src/rougail/output_doc/changelog.py:139
msgid "New variables"
msgstr ""
#: src/rougail/output_doc/changelog.py:150
msgid "Modified variable"
msgstr ""
#: src/rougail/output_doc/changelog.py:152
msgid "Modified variables"
msgstr ""
#: src/rougail/output_doc/changelog.py:163
msgid "Deleted variable"
msgstr ""
#: src/rougail/output_doc/changelog.py:165
msgid "Deleted variables"
msgstr ""
#: src/rougail/output_doc/config.py:43
msgid "duplicated level rougail-doc for output \"{0}\": {1} and {2}"
msgstr ""
#: src/rougail/output_doc/config.py:75
msgid "Configuration rougail-doc"
msgstr ""
#: src/rougail/output_doc/config.py:83
msgid "Starting title level"
msgstr ""
#: src/rougail/output_doc/config.py:88
msgid "Generated content"
msgstr ""
#: src/rougail/output_doc/config.py:97
msgid "Previous description file in JSON format"
msgstr ""
#: src/rougail/output_doc/config.py:105
msgid "Do not add families in documentation"
msgstr ""
#: src/rougail/output_doc/config.py:114
msgid "Disable documentation for variables with those modes"
msgstr ""
#: src/rougail/output_doc/config.py:122
msgid "disabled when there is no mode available"
msgstr ""
#: src/rougail/output_doc/config.py:128
msgid "verify if disable modes already exists"
msgstr ""
#: src/rougail/output_doc/config.py:130
msgid "Modify values to document leaderships and dynamics families"
msgstr ""
#: src/rougail/output_doc/config.py:133
msgid "Generate document in format"
msgstr ""
#: src/rougail/output_doc/config.py:151
msgid "Force true color terminal"
msgstr ""
#: src/rougail/output_doc/doc.py:344
msgid "This family contains lists of variable blocks."
msgstr ""
#: src/rougail/output_doc/doc.py:354
msgid "This family builds families dynamically."
msgstr ""
#: src/rougail/output_doc/doc.py:368
msgid "Default"
msgstr ""
#: src/rougail/output_doc/doc.py:383
msgid "multiple"
msgstr ""
#: src/rougail/output_doc/doc.py:391
msgid "Example"
msgstr ""
#: src/rougail/output_doc/doc.py:394
msgid "Examples"
msgstr ""
#: src/rougail/output_doc/doc.py:403
msgid "Tag"
msgstr ""
#: src/rougail/output_doc/doc.py:406
msgid "Tags"
msgstr ""
#: src/rougail/output_doc/doc.py:435
msgid "No attribute \"description\" for \"{0}\" in {1}"
msgstr ""
#: src/rougail/output_doc/doc.py:577
msgid "text based with regular expressions \"{0}\""
msgstr ""
#: src/rougail/output_doc/doc.py:581
msgid "Validator"
msgstr ""
#: src/rougail/output_doc/doc.py:584
msgid "Validators"
msgstr ""
#: src/rougail/output_doc/doc.py:593
msgid "Choices"
msgstr ""
#: src/rougail/output_doc/doc.py:696
msgid "the value of the identifier"
msgstr ""
#: src/rougail/output_doc/doc.py:700
msgid "the value of the {0}"
msgstr ""
#: src/rougail/output_doc/doc.py:709
msgid "depends on a calculation"
msgstr ""
#: src/rougail/output_doc/doc.py:715
msgid "\"{0}\" is a calculation for {1} but has no description in {2}"
msgstr ""
#: src/rougail/output_doc/doc.py:742 src/rougail/output_doc/doc.py:858
msgid "depends on an undocumented variable"
msgstr ""
#: src/rougail/output_doc/doc.py:758
msgid "when the variable \"{0}\" hasn't the value \"{1}\""
msgstr ""
#: src/rougail/output_doc/doc.py:760
msgid "when the variable \"{0}\" is defined and hasn't the value \"{1}\""
msgstr ""
#: src/rougail/output_doc/doc.py:765
msgid "when the variable \"{0}\" has the value \"{1}\""
msgstr ""
#: src/rougail/output_doc/doc.py:767
msgid "when the variable \"{0}\" is defined and has the value \"{1}\""
msgstr ""
#: src/rougail/output_doc/doc.py:785
msgid "the value of the variable \"{0}\""
msgstr ""
#: src/rougail/output_doc/doc.py:787
msgid "the value of the variable \"{0}\" if it is defined"
msgstr ""
#: src/rougail/output_doc/doc.py:788
msgid "the value of an undocumented variable"
msgstr ""
#: src/rougail/output_doc/doc.py:826
msgid "the values of undocumented variables"
msgstr ""
#: src/rougail/output_doc/doc.py:846
msgid "(from an undocumented variable)"
msgstr ""
#: src/rougail/output_doc/doc.py:854
msgid "{0} (from an undocumented variable)"
msgstr ""
#: src/rougail/output_doc/example.py:37
msgid "Example with mandatory variables not filled in"
msgstr ""
#: src/rougail/output_doc/example.py:42
msgid "Example with all variables modifiable"
msgstr ""
#: src/rougail/output_doc/output/console.py:164
#: src/rougail/output_doc/utils.py:782
msgid "Variable"
msgstr ""
#: src/rougail/output_doc/output/console.py:165
#: src/rougail/output_doc/utils.py:782
msgid "Description"
msgstr ""
#: src/rougail/output_doc/output/gitlab.py:51
msgid "Informations"
msgstr ""
#: src/rougail/output_doc/utils.py:44
msgid "the domain name can starts by a dot" msgid "the domain name can starts by a dot"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:45 #: src/rougail/output_doc/__init__.py:41
msgid "the domain name can be a hostname" msgid "the domain name can be a hostname"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:46 #: src/rougail/output_doc/__init__.py:42
msgid "the domain name can be an IP" msgid "the domain name can be an IP"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:47 #: src/rougail/output_doc/__init__.py:43
msgid "the domain name can be network in CIDR format" msgid "the domain name can be network in CIDR format"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:52 src/rougail/output_doc/utils.py:58 #: src/rougail/output_doc/__init__.py:48
msgid "the minimum value is {0}" msgid "the minimum value is {0}"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:53 src/rougail/output_doc/utils.py:59 #: src/rougail/output_doc/__init__.py:49
msgid "the maximum value is {0}" msgid "the maximum value is {0}"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:65 #: src/rougail/output_doc/__init__.py:55
msgid "IP must be in CIDR format" msgid "IP must be in CIDR format"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:66 #: src/rougail/output_doc/__init__.py:56
msgid "private IP are allowed" msgid "private IP are allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:67 #: src/rougail/output_doc/__init__.py:57
msgid "reserved IP are allowed" msgid "reserved IP are allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:72 #: src/rougail/output_doc/__init__.py:62
msgid "network must be in CIDR format"
msgstr ""
#: src/rougail/output_doc/utils.py:77
msgid "the host name can be an IP" msgid "the host name can be an IP"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:82 #: src/rougail/output_doc/__init__.py:67
msgid "the domain name in web address can be an IP" msgid "the domain name in web address can be an IP"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:83 #: src/rougail/output_doc/__init__.py:68
msgid "the domain name in web address can be only a hostname" msgid "the domain name in web address can be only a hostname"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:90 #: src/rougail/output_doc/__init__.py:75
msgid "can be range of port" msgid "can be range of port"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:91 #: src/rougail/output_doc/__init__.py:76
msgid "can have the protocol" msgid "can have the protocol"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:92 #: src/rougail/output_doc/__init__.py:77
msgid "port 0 is allowed" msgid "port 0 is allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:93 #: src/rougail/output_doc/__init__.py:78
msgid "well-known ports (1 to 1023) are allowed" msgid "ports 1 to 1023 are allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:94 #: src/rougail/output_doc/__init__.py:79
msgid "registred ports (1024 to 49151) are allowed" msgid "ports 1024 to 49151 are allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:95 #: src/rougail/output_doc/__init__.py:80
msgid "private ports (greater than 49152) are allowed" msgid "ports greater than 49152 are allowed"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:100 #: src/rougail/output_doc/__init__.py:125
msgid "minimum length for the secret is {0} characters" msgid "mandatory"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:101 #: src/rougail/output_doc/__init__.py:126
msgid "maximum length for the secret is {0} characters" msgid "hidden"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:102 #: src/rougail/output_doc/__init__.py:127
msgid "forbidden characters: {0}" msgid "disabled"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:107 #: src/rougail/output_doc/__init__.py:128
msgid "this filename could be a relative path" msgid "unique"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:108 #: src/rougail/output_doc/__init__.py:129
msgid "this file must exists" msgid "auto modified"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:109 #: src/rougail/output_doc/__init__.py:163
msgid "file type allowed: {0}"
msgstr ""
#: src/rougail/output_doc/utils.py:332
msgid "Variables for \"{0}\"" msgid "Variables for \"{0}\""
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:362 #: src/rougail/output_doc/__init__.py:180
msgid "Identifiers" msgid "Variables"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:659 src/rougail/output_doc/utils.py:668 #: src/rougail/output_doc/__init__.py:187
#: src/rougail/output_doc/utils.py:674 src/rougail/output_doc/utils.py:680 msgid "Example with mandatory variables not filled in"
#: src/rougail/output_doc/utils.py:684 msgstr ""
#: src/rougail/output_doc/__init__.py:493
msgid "(default)" msgid "(default)"
msgstr "" msgstr ""
#: src/rougail/output_doc/utils.py:832 #: src/rougail/output_doc/__init__.py:498
msgid "{0}: {1}" msgid "Choices"
msgstr ""
#: src/rougail/output_doc/__init__.py:501
msgid "Default"
msgstr ""
#: src/rougail/output_doc/__init__.py:559
msgid "the value of the variable \"{0}\""
msgstr ""
#: src/rougail/output_doc/__init__.py:564
#: src/rougail/output_doc/__init__.py:566
msgid "value of the {0}"
msgstr ""
#: src/rougail/output_doc/__init__.py:638
#: src/rougail/output_doc/__init__.py:651
msgid "Example"
msgstr ""
#: src/rougail/output_doc/__init__.py:649
msgid "Examples"
msgstr "" msgstr ""

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "rougail.output_doc" name = "rougail.output_doc"
version = "0.2.0a35" version = "0.1.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}] authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "Rougail output doc" description = "Rougail output doc"
@ -13,10 +13,11 @@ license = {file = "LICENSE"}
classifiers = [ classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python", "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.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Natural Language :: English", "Natural Language :: English",
@ -24,9 +25,8 @@ classifiers = [
] ]
dependencies = [ dependencies = [
"rougail-base > 1.1,<2", "rougail ~= 1.1,<2",
"tabulate ~= 0.9.0", "tabulate ~= 0.9.0",
"rich ~= 13.9.3",
] ]
[project.urls] [project.urls]
@ -37,9 +37,5 @@ name = "cz_conventional_commits"
tag_format = "$version" tag_format = "$version"
version_scheme = "pep440" version_scheme = "pep440"
version_provider = "pep621" version_provider = "pep621"
version_files = [ #update_changelog_on_bump = true
"src/rougail/output_doc/__version__.py",
"pyproject.toml:version"
]
update_changelog_on_bump = true
changelog_merge_prerelease = true changelog_merge_prerelease = true

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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
@ -15,9 +16,671 @@ details.
You should have received a copy of the GNU Lesser General Public License 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/>.
""" """
# FIXME si plusieurs example dont le 1er est none tester les autres : tests/dictionaries/00_8test_none
from tiramisu import Calculation
from tiramisu.error import display_list
import tabulate as tabulate_module
from tabulate import tabulate
from warnings import warn
from typing import Optional
from .doc import RougailOutputDoc from rougail.error import display_xmlfiles
from .__version__ import __version__ from rougail import RougailConfig, Rougail, CONVERT_OPTION
from rougail.object_model import PROPERTY_ATTRIBUTE
from .config import OutPuts
from .i18n import _
ENTER = "\n\n"
DocTypes = {
"domainname": {
"params": {
"allow_startswith_dot": _("the domain name can starts by a dot"),
"allow_without_dot": _("the domain name can be a hostname"),
"allow_ip": _("the domain name can be an IP"),
"allow_cidr_network": _("the domain name can be network in CIDR format"),
},
},
"number": {
"params": {
"min_number": _("the minimum value is {0}"),
"max_number": _("the maximum value is {0}"),
},
},
"ip": {
"msg": "IP",
"params": {
"cidr": _("IP must be in CIDR format"),
"private_only": _("private IP are allowed"),
"allow_reserved": _("reserved IP are allowed"),
},
},
"hostname": {
"params": {
"allow_ip": _("the host name can be an IP"),
},
},
"web_address": {
"params": {
"allow_ip": _("the domain name in web address can be an IP"),
"allow_without_dot": _(
"the domain name in web address can be only a hostname"
),
},
},
"port": {
"params": {
"allow_range": _("can be range of port"),
"allow_protocol": _("can have the protocol"),
"allow_zero": _("port 0 is allowed"),
"allow_wellknown": _("ports 1 to 1023 are allowed"),
"allow_registred": _("ports 1024 to 49151 are allowed"),
"allow_private": _("ports greater than 49152 are allowed"),
},
},
}
ROUGAIL_VARIABLE_TYPE = (
"https://rougail.readthedocs.io/en/latest/variable.html#variables-types"
)
class RougailOutputDoc:
def __init__(
self,
*,
config: "Config" = None,
rougailconfig: RougailConfig = None,
**kwarg,
):
if rougailconfig is None:
rougailconfig = RougailConfig
if rougailconfig["step.output"] != "doc":
rougailconfig["step.output"] = "doc"
if rougailconfig["step.output"] != "doc":
raise Exception("doc is not set as step.output")
self.rougailconfig = rougailconfig
outputs = OutPuts().get()
output = self.rougailconfig["doc.output_format"]
if output not in outputs:
raise Exception(
f'cannot find output "{output}", available outputs: {list(outputs)}'
)
if config is None:
rougail = Rougail(self.rougailconfig)
rougail.converted.plugins.append("output_doc")
config = rougail.get_config()
self.conf = config
self.conf.property.setdefault(frozenset({"advanced"}), "read_write", "append")
self.conf.property.read_write()
self.conf.property.remove("cache")
self.dynamic_paths = {}
self.formater = outputs[output]()
self.level = self.rougailconfig["doc.title_level"]
# self.property_to_string = [('mandatory', 'obligatoire'), ('hidden', 'cachée'), ('disabled', 'désactivée'), ('unique', 'unique'), ('force_store_value', 'modifié automatiquement')]
self.property_to_string = [
("mandatory", _("mandatory")),
("hidden", _("hidden")),
("disabled", _("disabled")),
("unique", _("unique")),
("force_store_value", _("auto modified")),
]
def run(self):
print(self.gen_doc())
def gen_doc(self):
tabulate_module.PRESERVE_WHITESPACE = True
examples_mini = {}
examples_all = {}
return_string = self.formater.header()
if self.rougailconfig["main_namespace"]:
for namespace in self.conf.unrestraint.list():
name = namespace.name()
examples_mini[name] = {}
examples_all[name] = {}
doc = (
self._display_doc(
self.display_families(
namespace,
self.level + 1,
examples_mini[name],
examples_all[name],
),
[],
)
+ "\n"
)
if not examples_mini[name]:
del examples_mini[name]
if not examples_all[name]:
del examples_all[name]
else:
return_string += self.formater.title(
_('Variables for "{0}"').format(namespace.name()), self.level
)
return_string += doc
else:
doc = (
self._display_doc(
self.display_families(
self.conf.unrestraint,
self.level + 1,
examples_mini,
examples_all,
),
[],
)
+ "\n"
)
if examples_all:
return_string += self.formater.title(_("Variables"), self.level)
return_string += doc
if not examples_all:
return ""
if self.rougailconfig["doc.with_example"]:
if examples_mini:
return_string += self.formater.title(
_("Example with mandatory variables not filled in"), self.level
)
return_string += self.formater.yaml(examples_mini)
if examples_all:
return_string += self.formater.title(
"Example with all variables modifiable", self.level
)
return_string += self.formater.yaml(examples_all)
return return_string
def _display_doc(self, variables, add_paths):
return_string = ""
for variable in variables:
typ = variable["type"]
path = variable["path"]
if path in add_paths:
continue
if typ == "family":
return_string += variable["title"]
return_string += self._display_doc(variable["objects"], add_paths)
else:
for idx, path in enumerate(variable["paths"]):
if path in self.dynamic_paths:
paths_msg = display_list(
[
self.formater.bold(path_)
for path_ in self.dynamic_paths[path]["paths"]
],
separator="or",
)
variable["objects"][idx][0] = variable["objects"][idx][
0
].replace("{{ ROUGAIL_PATH }}", paths_msg)
identifiers = self.dynamic_paths[path]["identifiers"]
description = variable["objects"][idx][1][0]
if "{{ identifier }}" in description:
if description.endswith("."):
description = description[:-1]
comment_msg = self.to_phrase(
display_list(
[
description.replace(
"{{ identifier }}",
self.formater.italic(identifier),
)
for identifier in identifiers
],
separator="or",
add_quote=True,
)
)
variable["objects"][idx][1][0] = comment_msg
variable["objects"][idx][1] = self.formater.join(
variable["objects"][idx][1]
)
return_string += (
self.formater.table(
tabulate(
variable["objects"],
headers=self.formater.table_header(
["Variable", "Description"]
),
tablefmt=self.formater.name,
)
)
+ "\n\n"
)
add_paths.append(path)
return return_string
def is_hidden(self, child):
properties = child.property.get(uncalculated=True)
for hidden_property in ["hidden", "disabled", "advanced"]:
if hidden_property in properties:
return True
return False
def display_families(
self,
family,
level,
examples_mini,
examples_all,
):
variables = []
for child in family.list():
if self.is_hidden(child):
continue
if not child.isoptiondescription():
if child.isfollower() and child.index() != 0:
# only add to example
self.display_variable(
child,
examples_mini,
examples_all,
)
continue
path = child.path(uncalculated=True)
if child.isdynamic():
self.dynamic_paths.setdefault(
path, {"paths": [], "identifiers": []}
)["paths"].append(child.path())
self.dynamic_paths[path]["identifiers"].append(
child.identifiers()[-1]
)
if not variables or variables[-1]["type"] != "variables":
variables.append(
{
"type": "variables",
"objects": [],
"path": path,
"paths": [],
}
)
variables[-1]["objects"].append(
self.display_variable(
child,
examples_mini,
examples_all,
)
)
variables[-1]["paths"].append(path)
else:
name = child.name()
if child.isleadership():
examples_mini[name] = []
examples_all[name] = []
else:
examples_mini[name] = {}
examples_all[name] = {}
variables.append(
{
"type": "family",
"title": self.display_family(
child,
level,
),
"path": child.path(uncalculated=True),
"objects": self.display_families(
child,
level + 1,
examples_mini[name],
examples_all[name],
),
}
)
if not examples_mini[name]:
del examples_mini[name]
if not examples_all[name]:
del examples_all[name]
return variables
def display_family(
self,
family,
level,
):
if family.name() != family.description(uncalculated=True):
title = f"{family.description(uncalculated=True)}"
else:
warning = f'No attribute "description" for family "{family.path()}" in {display_xmlfiles(family.information.get("dictionaries"))}'
warn(warning)
title = f"{family.path()}"
isdynamic = family.isdynamic(only_self=True)
if isdynamic:
identifiers = family.identifiers(only_self=True)
if "{{ identifier }}" in title:
title = display_list(
[
title.replace(
"{{ identifier }}", self.formater.italic(identifier)
)
for identifier in identifiers
],
separator="or",
add_quote=True,
)
msg = self.formater.title(title, level)
subparameter = []
self.manage_properties(family, subparameter)
if subparameter:
msg += self.subparameter_to_string(subparameter) + ENTER
comment = []
self.subparameter_to_parameter(subparameter, comment)
if comment:
msg += "\n".join(comment) + ENTER
help = self.to_phrase(family.information.get("help", ""))
if help:
msg += "\n" + help + ENTER
if family.isleadership():
# help = "Cette famille contient des listes de bloc de variables."
help = "This family contains lists of variable blocks."
msg += "\n" + help + ENTER
if isdynamic:
identifiers = family.identifiers(only_self=True, uncalculated=True)
if isinstance(identifiers, Calculation):
identifiers = self.to_string(family, "dynamic")
if isinstance(identifiers, list):
for idx, val in enumerate(identifiers):
if not isinstance(val, Calculation):
continue
identifiers[idx] = self.to_string(family, "dynamic", f"_{idx}")
identifiers = self.formater.list(identifiers)
# help = f"Cette famille construit des familles dynamiquement.\n\n{self.formater.bold('Identifiers')}: {identifiers}"
help = f"This family builds families dynamically.\n\n{self.formater.bold('Identifiers')}: {identifiers}"
msg += "\n" + help + ENTER
return msg
def manage_properties(
self,
variable,
subparameter,
):
properties = variable.property.get(uncalculated=True)
for mode in self.rougailconfig["modes_level"]:
if mode in properties:
subparameter.append((self.formater.prop(mode), None, None))
break
for prop, msg in self.property_to_string:
if prop in properties:
subparameter.append((self.formater.prop(msg), None, None))
elif variable.information.get(f"{prop}_calculation", False):
subparameter.append(
(self.formater.prop(msg), msg, self.to_string(variable, prop))
)
def subparameter_to_string(
self,
subparameter,
):
subparameter_str = ""
for param in subparameter:
if param[1]:
subparameter_str += f"_{param[0]}_ "
else:
subparameter_str += f"{param[0]} "
return subparameter_str[:-1]
def subparameter_to_parameter(
self,
subparameter,
comment,
):
for param in subparameter:
if not param[1]:
continue
msg = param[2]
comment.append(f"{self.formater.bold(param[1].capitalize())}: {msg}")
def to_phrase(self, msg):
if not msg:
return ""
msg = str(msg).strip()
if not msg.endswith("."):
msg += "."
return msg[0].upper() + msg[1:]
def display_variable(
self,
variable,
examples_mini,
examples_all,
):
if variable.isdynamic():
parameter = ["{{ ROUGAIL_PATH }}"]
else:
parameter = [f"{self.formater.bold(variable.path())}"]
subparameter = []
description = variable.description(uncalculated=True)
comment = [self.to_phrase(description)]
help_ = self.to_phrase(variable.information.get("help", ""))
if help_:
comment.append(help_)
self.type_to_string(
variable,
subparameter,
comment,
)
self.manage_properties(
variable,
subparameter,
)
if variable.ismulti():
multi = not variable.isfollower() or variable.issubmulti()
else:
multi = False
if multi:
subparameter.append((self.formater.prop("multiple"), None, None))
if subparameter:
parameter.append(self.subparameter_to_string(subparameter))
if variable.name() == description:
warning = f'No attribute "description" for variable "{variable.path()}" in {display_xmlfiles(variable.information.get("dictionaries"))}'
warn(warning)
default = self.get_default(
variable,
comment,
)
default_in_choices = False
if variable.information.get("type") == "choice":
choices = variable.value.list(uncalculated=True)
if isinstance(choices, Calculation):
choices = self.to_string(variable, "choice")
if isinstance(choices, list):
for idx, val in enumerate(choices):
if not isinstance(val, Calculation):
if default is not None and val == default:
choices[idx] = str(val) + "" + _("(default)")
default_in_choices = True
continue
choices[idx] = self.to_string(variable, "choice", f"_{idx}")
choices = self.formater.list(choices)
comment.append(f'{self.formater.bold(_("Choices"))}: {choices}')
# choice
if default is not None and not default_in_choices:
comment.append(f"{self.formater.bold(_('Default'))}: {default}")
self.manage_exemples(
multi,
variable,
examples_all,
examples_mini,
comment,
)
self.subparameter_to_parameter(subparameter, comment)
self.formater.columns(parameter, comment)
return [self.formater.join(parameter), comment]
def get_default(
self,
variable,
comment,
):
if variable.information.get("fake_default", False):
default = None
else:
default = variable.value.get(uncalculated=True)
if default in [None, []]:
return
if isinstance(default, Calculation):
default = self.to_string(variable, "default")
if isinstance(default, list):
for idx, val in enumerate(default):
if not isinstance(val, Calculation):
continue
default[idx] = self.to_string(variable, "default", f"_{idx}")
default = self.formater.list(default)
return default
def to_string(
self,
variable,
prop,
identifier="",
):
calculation_type = variable.information.get(
f"{prop}_calculation_type{identifier}", None
)
if not calculation_type:
raise Exception(
f"cannot find {prop}_calculation_type{identifier} information, do you have declare doc has a plugins?"
)
calculation = variable.information.get(f"{prop}_calculation{identifier}")
if calculation_type == "jinja":
if calculation is not True:
values = self.formater.to_string(calculation)
else:
values = "depends on a calculation"
warning = f'"{prop}" is a calculation for {variable.path()} but has no description in {display_xmlfiles(variable.information.get("dictionaries"))}'
warn(warning)
elif calculation_type == "variable":
if prop in PROPERTY_ATTRIBUTE:
values = self.formater.to_string(calculation)
else:
values = _('the value of the variable "{0}"').format(calculation)
elif calculation_type == "identifier":
if prop in PROPERTY_ATTRIBUTE:
values = self.formater.to_string(calculation)
else:
values = _("value of the {0}").format(calculation_type)
else:
values = _("value of the {0}").format(calculation_type)
if not values.endswith("."):
values += "."
return values
def type_to_string(
self,
variable,
subparameter,
comment,
):
variable_type = variable.information.get("type")
doc_type = DocTypes.get(variable_type, {"params": {}})
subparameter.append(
(
self.formater.link(
doc_type.get("msg", variable_type), ROUGAIL_VARIABLE_TYPE
),
None,
)
)
option = variable.get()
validators = []
for param, msg in doc_type["params"].items():
value = option.impl_get_extra(f"_{param}")
if value is None:
value = option.impl_get_extra(param)
if value is not None and value is not False:
validators.append(msg.format(value))
valids = [
name
for name in variable.information.list()
if name.startswith("validators_calculation_type_")
]
if valids:
for idx in range(len(valids)):
validators.append(
self.to_string(
variable,
"validators",
f"_{idx}",
)
)
if validators:
if len(validators) == 1:
comment.append(f'{self.formater.bold("Validator")}: ' + validators[0])
else:
comment.append(
f'{self.formater.bold("Validators")}:'
+ self.formater.list(validators)
)
def manage_exemples(
self,
multi,
variable,
examples_all,
examples_mini,
comment,
):
example_mini = None
example_all = None
example = variable.information.get("examples", None)
if example is None:
example = variable.information.get("test", None)
default = variable.value.get()
if isinstance(example, tuple):
example = list(example)
mandatory = "mandatory" in variable.property.get(uncalculated=True)
if example:
if not multi:
example = example[0]
title = _("Example")
if mandatory:
example_mini = example
example_all = example
else:
if mandatory:
example_mini = "\n - example"
example_all = example
len_test = len(example)
example = self.formater.list(example)
if len_test > 1:
title = _("Examples")
else:
title = _("Example")
comment.append(f"{self.formater.bold(title)}: {example}")
elif default not in [None, []]:
example_all = default
else:
example = CONVERT_OPTION.get(variable.information.get("type"), {}).get(
"example", None
)
if example is None:
example = "xxx"
if multi:
example = [example]
if mandatory:
example_mini = example
example_all = example
if variable.isleader():
if example_mini is not None:
for mini in example_mini:
examples_mini.append({variable.name(): mini})
if example_all is not None:
for mall in example_all:
examples_all.append({variable.name(): mall})
elif variable.isfollower():
if example_mini is not None:
for idx in range(0, len(examples_mini)):
examples_mini[idx][variable.name()] = example_mini
if example_all is not None:
for idx in range(0, len(examples_all)):
examples_all[idx][variable.name()] = example_all
else:
if example_mini is not None:
examples_mini[variable.name()] = example_mini
examples_all[variable.name()] = example_all
RougailOutput = RougailOutputDoc RougailOutput = RougailOutputDoc

View file

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

View file

@ -1,6 +1,7 @@
""" """Annotate for documentation
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -19,13 +20,12 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" """
from typing import Union from tiramisu import undefined
from rougail.utils import undefined, PROPERTY_ATTRIBUTE
from rougail.annotator.variable import Walk from rougail.annotator.variable import Walk
from rougail.output_doc.i18n import _ from rougail.i18n import _
from rougail.convert.object_model import ( from rougail.error import DictConsistencyError
from rougail.object_model import (
Calculation, Calculation,
JinjaCalculation, JinjaCalculation,
VariableCalculation, VariableCalculation,
@ -34,8 +34,8 @@ from rougail.convert.object_model import (
IdentifierPropertyCalculation, IdentifierPropertyCalculation,
InformationCalculation, InformationCalculation,
IndexCalculation, IndexCalculation,
NamespaceCalculation,
CONVERT_OPTION, CONVERT_OPTION,
PROPERTY_ATTRIBUTE,
) )
@ -47,140 +47,113 @@ class Annotator(Walk):
def __init__( def __init__(
self, self,
objectspace, objectspace,
*args, # pylint: disable=unused-argument *args,
) -> None: ) -> None:
if not objectspace.paths: if not objectspace.paths:
return return
self.objectspace = objectspace self.objectspace = objectspace
self.change_default_value = self.objectspace.rougailconfig[
"doc.change_default_value"
]
self.populate_family() self.populate_family()
self.populate_variable() self.populate_variable()
def populate_family(self) -> None: def get_examples_values(self, variable):
"""Set doc, path, ... to family"""
for family in self.get_families():
self.add_informations_from_properties(family)
if family.type == "dynamic":
self.force_default_value_in_suffix_variable(family)
self.calculation_to_information(
family.path,
"dynamic",
family.dynamic,
family.version,
)
path = family.path
if path in self.objectspace.forced_descriptions:
self.objectspace.informations.add(
path,
"forced_description",
True,
)
def force_default_value_in_suffix_variable(self, family) -> None:
if not self.change_default_value:
return
if not isinstance(family.dynamic, list):
self._force_default_value_in_suffix_variable(family, family.dynamic)
else:
for value in family.dynamic:
self._force_default_value_in_suffix_variable(
family, value, return_a_list=False
)
def _force_default_value_in_suffix_variable(
self,
family,
value,
*,
return_a_list=True,
) -> None:
"""For dynamic we must have values to document it"""
if not isinstance(value, Calculation):
return
value_added = False
if isinstance(value, (VariableCalculation, VariablePropertyCalculation)):
variable = self.objectspace.paths.get_with_dynamic(
value.variable,
family.path,
value.version,
value.namespace,
value.xmlfiles,
)[0]
if variable and not variable.default:
values = self.add_examples_values(variable)
value_added = True
if not value_added:
default_values = ["example"]
if not return_a_list:
default_values = default_values[0]
value.default_values = default_values
def add_examples_values(self, variable) -> list:
"""Check examples or test information to define examples values in a variable"""
values = self.objectspace.informations.get(variable.path).get("examples", None) values = self.objectspace.informations.get(variable.path).get("examples", None)
if not values: if not values:
values = self.objectspace.informations.get(variable.path).get("test", None) values = self.objectspace.informations.get(variable.path).get("test", None)
if isinstance(values, tuple): if isinstance(values, tuple):
values = list(values) values = list(values)
if values:
variable.default = list(values)
else:
variable.default = [CONVERT_OPTION[variable.type]["example"]]
self.objectspace.informations.add(
variable.path, "default_value_makes_sense", False
)
return values return values
def add_default_value(
self,
family,
value,
*,
inside_list=False,
) -> None:
if isinstance(value, Calculation):
default_values = "example"
if not inside_list:
default_values = [default_values]
if isinstance(value, (VariableCalculation, VariablePropertyCalculation)):
variable, identifier = self.objectspace.paths.get_with_dynamic(
value.variable,
value.path_prefix,
family.path,
value.version,
value.namespace,
value.xmlfiles,
)
values = self.get_examples_values(variable)
if values:
if inside_list:
default_values = list(values)
else:
default_values = values
value.default_values = default_values
def populate_family(self) -> None:
"""Set doc, path, ... to family"""
for family in self.get_families():
self.objectspace.informations.add(
family.path, "dictionaries", family.xmlfiles
)
self.convert_variable_property(family)
if family.type != "dynamic":
continue
if not isinstance(family.dynamic, list):
self.add_default_value(family, family.dynamic)
else:
for value in family.dynamic:
self.add_default_value(family, value, inside_list=True)
self.calculation_to_information(
family.path,
"dynamic",
family.dynamic,
family.version,
)
def populate_variable(self) -> None: def populate_variable(self) -> None:
"""convert variables""" """convert variables"""
for variable in self.get_variables(): for variable in self.get_variables():
if variable.type == "symlink": if variable.type == "symlink":
continue continue
path = variable.path
if variable.type == "choice": if variable.type == "choice":
self.calculation_to_information( self.calculation_to_information(
path, variable.path,
"choice", "choice",
variable.choices, variable.choices,
variable.version, variable.version,
) )
default = variable.default
if default is None and path in self.objectspace.default_multi:
default = self.objectspace.default_multi[path]
self.calculation_to_information( self.calculation_to_information(
path, variable.path,
"default", "default",
default, variable.default,
variable.version, variable.version,
) )
self.calculation_to_information( self.calculation_to_information(
path, variable.path,
"validators", "validators",
variable.validators, variable.validators,
variable.version, variable.version,
) )
if ( if variable.path in self.objectspace.leaders and not variable.default:
self.change_default_value values = self.get_examples_values(variable)
and path in self.objectspace.leaders if values:
and not default variable.default = list(values)
): else:
self.add_examples_values(variable) variable.default = [CONVERT_OPTION[variable.type]["example"]]
self.add_informations_from_properties(variable) self.objectspace.informations.add(variable.path, "fake_default", True)
if path in self.objectspace.forced_descriptions: self.objectspace.informations.add(
self.objectspace.informations.add( variable.path, "dictionaries", variable.xmlfiles
path, )
"forced_description", self.convert_variable_property(variable)
True,
)
def add_informations_from_properties( def convert_variable_property(
self, self,
variable: dict, variable: dict,
) -> None: ) -> None:
"""convert properties""" """convert properties"""
for prop in PROPERTY_ATTRIBUTE: for prop in ["hidden", "disabled", "mandatory"]:
prop_value = getattr(variable, prop, None) prop_value = getattr(variable, prop, None)
if not prop_value: if not prop_value:
continue continue
@ -190,8 +163,6 @@ class Annotator(Walk):
prop_value, prop_value,
variable.version, variable.version,
) )
if isinstance(prop_value, Calculation):
prop_value.default_values = False
def calculation_to_information( def calculation_to_information(
self, self,
@ -200,150 +171,76 @@ class Annotator(Walk):
values, values,
version: str, version: str,
): ):
"""tranform calculation to an information""" self._calculation_to_information(
one_is_calculation = False path,
if not isinstance(values, list): prop,
if not isinstance(values, Calculation): values,
return version,
one_is_calculation = True )
datas = self.calculation_to_string(path, prop, values, version) if isinstance(values, list):
else:
datas = []
for idx, val in enumerate(values): for idx, val in enumerate(values):
data = self.calculation_to_string(path, prop, val, version) self._calculation_to_information(
if data is None: path,
continue prop,
if "type" in data or "description" in data: val,
one_is_calculation = True version,
datas.append(data) identifier=f"_{idx}",
if one_is_calculation: )
self.objectspace.informations.add(
path,
f"{prop}_calculation",
datas,
)
def calculation_to_string( def _calculation_to_information(
self, self,
path: str, path: str,
prop: str, prop: str,
values, values,
version: str, version: str,
*,
identifier: str = "",
): ):
if not isinstance(values, Calculation): if not isinstance(values, Calculation):
return {"value": values} return
if values.description: values_calculation = True
return {"description": values.description}
if isinstance(values, JinjaCalculation): if isinstance(values, JinjaCalculation):
if values.description: if values.description:
value = values.description values_calculation = values.description
else: values_calculation_type = "jinja"
value = True elif isinstance(values, (VariableCalculation, VariablePropertyCalculation)):
return { values_calculation = values.variable
"type": "jinja", paths = self.objectspace.paths
"value": value, if version != "1.0" and paths.regexp_relative.search(values_calculation):
} calculation_path = paths.get_full_path(
if isinstance(values, (VariableCalculation, VariablePropertyCalculation)): values_calculation,
variable_path = self.get_path_from_variable(values, version, path) path,
value = self.calculation_to_information_variable( )
variable_path, values, prop, version, path if prop in PROPERTY_ATTRIBUTE:
) if values.when is not undefined:
if value is None: values_calculation = f'when the variable "{calculation_path}" has the value "{values.when}"'
return elif values.when_not is not undefined:
return { values_calculation = f'when the variable "{calculation_path}" hasn\'t the value "{values.when_not}"'
"type": "variable", else:
"value": value, values_calculation = f'when the variable "{calculation_path}" has the value "True"'
"ori_path": variable_path, else:
"optional": values.optional, values_calculation = calculation_path
} values_calculation_type = "variable"
if isinstance(values, InformationCalculation): elif isinstance(values, InformationCalculation):
return { values_calculation_type = "information"
"type": "information", elif isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)):
"value": self.calculation_to_information_information( if version != "1.0" and prop in PROPERTY_ATTRIBUTE:
values, version, path if values.when is not undefined:
), values_calculation = f'when the identifier is "{values.when}"'
} elif values.when_not is not undefined:
if isinstance(values, (IdentifierCalculation, IdentifierPropertyCalculation)): values_calculation = (
return { f'when the identifier is not "{values.when_not}"'
"type": "identifier", )
"value": self.calculation_to_information_identifier( values_calculation_type = "identifier"
values, prop, version elif isinstance(values, IndexCalculation):
), values_calculation_type = "index"
} self.objectspace.informations.add(
if isinstance(values, IndexCalculation):
return {
"type": "index",
"value": True,
}
if isinstance(values, NamespaceCalculation):
return {
"type": "namespace",
"value": True,
}
raise Exception(f'unknown calculation {type(values)} "{values}"')
def calculation_to_information_variable(
self, variable_path: str, values, prop: str, version: str, path: str
) -> str:
# is optional
variable = self.objectspace.paths.get_with_dynamic(
variable_path,
path, path,
values.version, f"{prop}_calculation_type{identifier}",
values.namespace, values_calculation_type,
values.xmlfiles, )
)[0] self.objectspace.informations.add(
if isinstance(values, VariableCalculation) and values.optional and not variable: path,
return None f"{prop}_calculation{identifier}",
if variable: values_calculation,
variable_path = variable.path )
if prop in PROPERTY_ATTRIBUTE:
# get comparative value
if values.when_not is not undefined:
value = values.when_not
condition = "when_not"
elif values.when is not undefined:
value = values.when
condition = "when"
else:
value = True
condition = "when"
# set message
values_calculation = (variable_path, value, condition)
else:
values_calculation = variable_path
return values_calculation
def calculation_to_information_information(
self, values, version: str, path: str
) -> Union[str, bool]:
if values.variable:
variable_path = self.get_path_from_variable(values, version, path)
return _('the value of the information "{0}" of the variable "{1}"').format(
values.information, variable_path
)
return _('the value of the global information "{0}"').format(values.information)
def calculation_to_information_identifier(
self, values, prop: str, version: str
) -> Union[str, bool]:
if version != "1.0" and prop in PROPERTY_ATTRIBUTE:
if values.when is not undefined:
return _('when the identifier is "{0}"').format(values.when)
if values.when_not is not undefined:
return _('when the identifier is not "{0}"').format(values.when_not)
return True
def get_path_from_variable(self, values, version: str, path: str) -> str:
variable_path = values.variable
paths = self.objectspace.paths
if version != "1.0" and paths.regexp_relative.search(variable_path):
variable_path = paths.get_full_path(
variable_path,
path,
)
elif version == "1.0" and "{{ suffix }}" in variable_path:
variable_path = variable_path.replace("{{ suffix }}", "{{ identifier }}")
return variable_path

View file

@ -1,174 +0,0 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 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 pathlib import Path
from json import loads
from .config import OutPuts
from .i18n import _
from .utils import calc_path
class Changelog: # pylint: disable=no-member,too-few-public-methods
"""Build changelog"""
def gen_doc_changelog(self):
"""Return changelog"""
with Path(self.previous_json_file).open() as outfh:
previous_doc = loads(outfh.read())
self.load()
self._added_variables = []
self._modified_variables = []
self._removed_variables = []
self.parser(previous_doc, self.informations)
return self.display()
def parser(self, previous_families, new_families):
def add(new):
self.formater.variable_to_string(new, self._added_variables)
def remove(previous):
self._removed_variables.append(previous)
done = []
for element in list(previous_families) + list(new_families):
if element in done:
continue
done.append(element)
previous = previous_families.get(element)
new = new_families.get(element)
if not previous:
if new["type"] == "variable":
add(new)
else:
self.parser({}, new["children"])
elif not new:
if previous["type"] == "variable":
if "identifiers" in previous:
for identifiers in previous["identifiers"]:
remove(calc_path(previous["path"], self.formater, identifiers))
else:
remove(calc_path(previous["path"], self.formater))
else:
self.parser(previous["children"], {})
elif previous["type"] != new["type"]:
if previous["type"] == "variable":
if "identifiers" in previous:
for identifiers in previous["identifiers"]:
remove(calc_path(previous["path"], self.formater, identifiers))
else:
remove(calc_path(previous["path"], self.formater))
self.parser({}, new["children"])
else:
add(new)
self.parser(previous["children"], {})
elif previous["type"] != "variable":
self.parser(previous["children"], new["children"])
else:
modified_attributes = {}
for prop in set(previous) | set(new):
prop_previous = previous.get(prop, [])
prop_new = new.get(prop, [])
if prop_previous != prop_new:
name = None
if (
isinstance(prop_previous, dict)
and "values" in prop_previous
):
name = prop_previous["name"]
local_prop_previous = prop_previous = prop_previous[
"values"
]
if not isinstance(prop_previous, list):
if prop == "default":
local_prop_previous = [prop_previous]
else:
local_prop_previous = prop_previous = [
prop_previous
]
else:
local_prop_previous = prop_previous
if isinstance(prop_new, dict) and "values" in prop_new:
name = prop_new["name"]
prop_new = prop_new["values"]
if not isinstance(prop_new, list):
prop_new = [prop_new]
if isinstance(prop_new, list):
prop_new = prop_new.copy()
else:
prop_new = [prop_new]
if isinstance(prop_previous, list):
prop_previous = [
p for p in prop_previous if p not in prop_new
]
elif prop_previous in prop_new:
prop_new.remove(prop_previous)
prop_previous = []
prop_new = [p for p in prop_new if p not in local_prop_previous]
if prop_previous not in [None, []] or prop_new not in [
None,
[],
]:
modified_attributes[prop] = (name, prop_previous, prop_new)
if not modified_attributes:
continue
self.formater.variable_to_string(
new, self._modified_variables, modified_attributes
)
def display(self) -> str:
msg = ""
if self._added_variables:
if len(self._added_variables) == 1:
title = _("New variable")
else:
title = _("New variables")
msg += self.formater.run(
[
self.formater.title(title, self.level),
self.formater.table(self._added_variables),
],
self.level,
dico_is_already_treated=True,
)
if self._modified_variables:
if len(self._modified_variables) == 1:
title = _("Modified variable")
else:
title = _("Modified variables")
msg += self.formater.run(
[
self.formater.title(title, self.level),
self.formater.table(self._modified_variables),
],
self.level,
dico_is_already_treated=True,
)
if self._removed_variables:
if len(self._removed_variables) == 1:
title = _("Deleted variable")
else:
title = _("Deleted variables")
msg += self.formater.run(
[
self.formater.title(title, self.level),
self.formater.list(self._removed_variables),
],
self.level,
dico_is_already_treated=True,
)
return msg

View file

@ -2,7 +2,7 @@
Config file for Rougail-doc Config file for Rougail-doc
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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
@ -20,29 +20,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from pathlib import Path from pathlib import Path
from rougail.utils import load_modules from rougail.utils import load_modules
from .i18n import _
OUTPUTS = None OUTPUTS = None
def get_outputs() -> None: def get_outputs() -> None:
"""Load all outputs""" module_name = "rougail.doc.output"
module_name = "rougail.output_doc.output"
outputs = {} outputs = {}
for path in (Path(__file__).parent / "output").iterdir(): for path in (Path(__file__).parent / "output").iterdir():
name = path.name name = path.name
if not name.endswith(".py") or name.endswith("__.py"): if not name.endswith(".py") or name.endswith("__.py"):
continue continue
module = load_modules(module_name + "." + name[:-3], str(path)) module = load_modules(module_name + "." + name, str(path))
if "Formater" not in dir(module): if "Formater" not in dir(module):
continue continue
level = module.Formater.level level = module.Formater.level
if level in outputs: if level in outputs:
raise ImportError( raise Exception(
_('duplicated level rougail-doc for output "{0}": {1} and {2}').format( f'duplicated level rougail-doc for output "{level}": {module.Formater.name} and {outputs[level].name}'
level, module.Formater.name, outputs[level].name
)
) )
outputs[module.Formater.level] = module.Formater outputs[module.Formater.level] = module.Formater
return {outputs[level].name: outputs[level] for level in sorted(outputs)} return {outputs[level].name: outputs[level] for level in sorted(outputs)}
@ -59,98 +55,43 @@ class OutPuts: # pylint: disable=R0903
OUTPUTS = get_outputs() OUTPUTS = get_outputs()
def get(self) -> dict: def get(self) -> dict:
"""Get all outputs"""
return OUTPUTS return OUTPUTS
def get_rougail_config( def get_rougail_config(
*, *,
backward_compatibility=True, # pylint: disable=unused-argument backward_compatibility=True,
) -> dict: ) -> dict:
"""Get documentation for output_doc modules"""
outputs = list(OutPuts().get()) outputs = list(OutPuts().get())
output_format_default = outputs[0] output_format_default = outputs[0]
rougail_options = f""" rougail_options = """
doc: doc:
description: {_('Configuration rougail-doc')} description: Configuration rougail-doc
disabled: disabled:
type: jinja
jinja: | jinja: |
{{% if step.output is propertyerror or step.output != 'doc' %}} {% if step.output != 'doc' %}
disabled disabled
{{% endif %}} {% endif %}
title_level: title_level:
description: {_('Starting title level')} description: Start title level
alternative_name: dt alternative_name: dt
default: 1 default: 1
with_example:
contents: description: Display example in documentation
description: {_('Generated content')} negative_description: Hide example in documentation
choices: alternative_name: de
- variables
- example
- changelog
default:
- variables
previous_json_file:
description: {_('Previous description file in JSON format')}
disabled:
jinja: |-
{{{{ "changelog" not in _.contents }}}}
return_type: boolean
description: changelog is not selected
without_family:
description: {_('Do not add families in documentation')}
default: false default: false
disabled:
jinja: |-
{{{{ "variables" not in _.contents and _.output_format != "json" }}}}
return_type: boolean
description: variables is not selected
disabled_modes:
description: {_('Disable documentation for variables with those modes')}
multi: true
mandatory: false
disabled:
jinja: |
{{% if not modes_level %}}
there is no mode
{{% endif %}}
description: {_('disabled when there is no mode available')}
validators:
- jinja: |
{{% if _.disabled_modes not in modes_level %}}
this mode is not available
{{% endif %}}
description: {_('verify if disable modes already exists')}
change_default_value: true # {_('Modify values to document leaderships and dynamics families')}
output_format: output_format:
description: {_('Generate document in format')} description: Generate document in format
alternative_name: do alternative_name: do
default: output_format_default default: output_format_default
validators:
- jinja: |-
{{% if _.output_format == 'json' %}}
{{% if "changelog" in _.contents %}}
cannot add to contents "changelog" with output_format "json"
{{% endif %}}
{{% endif %}}
choices: choices:
""".replace( """.replace(
"output_format_default", output_format_default "output_format_default", output_format_default
) )
for output in outputs: for output in outputs:
rougail_options += f" - {output}\n" rougail_options += f" - {output}\n"
rougail_options += f"""
force_true_color_terminal:
description: {_('Force true color terminal')}
default: false
"""
return { return {
"name": "doc", "name": "doc",
"process": "output", "process": "output",

View file

@ -1,876 +0,0 @@
"""
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 warnings import warn
from typing import Optional
from itertools import chain
from re import compile
from tiramisu import Calculation, groups
from tiramisu.error import ConfigError, display_list, PropertiesOptionError
from rougail.tiramisu import display_xmlfiles, normalize_family
from rougail.utils import undefined, get_properties_to_string, PROPERTY_ATTRIBUTE
from rougail.error import VariableCalculationDependencyError, RougailWarning
from .config import OutPuts
from .i18n import _
from .utils import DocTypes, dump, to_phrase, calc_path
from .example import Examples
from .changelog import Changelog
HIDDEN_PROPERTIES = [
"hidden",
"disabled",
]
class RougailOutputDoc(Examples, Changelog):
"""Rougail Output Doc:
Generate documentation from rougail description files
"""
def __init__(
self,
config: "Config",
*,
rougailconfig: "RougailConfig" = None,
**kwarg,
):
# Import here to avoid circular import
from rougail.tiramisu import CONVERT_OPTION
self.convert_option = CONVERT_OPTION
if rougailconfig is None:
from rougail import RougailConfig
rougailconfig = RougailConfig
if rougailconfig["step.output"] != "doc":
rougailconfig["step.output"] = "doc"
if rougailconfig["step.output"] != "doc":
raise Exception("doc is not set as step.output")
outputs = OutPuts().get()
output_format = rougailconfig["doc.output_format"]
if output_format not in outputs:
raise Exception(
f'cannot find output "{output_format}", available outputs: {list(outputs)}'
)
self.conf = config
self.modes_level = rougailconfig["modes_level"]
if self.modes_level:
self.disabled_modes = rougailconfig["doc.disabled_modes"]
if self.disabled_modes:
self.conf.property.setdefault(
frozenset(self.disabled_modes), "read_write", "append"
)
else:
self.disabled_modes = []
self.conf.property.read_write()
# self.conf.property.remove("cache")
self.output_format = output_format
self.level = rougailconfig["doc.title_level"]
self.contents = rougailconfig["doc.contents"]
self.example = "example" in self.contents
if "variables" in self.contents:
self.with_family = not rougailconfig["doc.without_family"]
else:
self.with_family = True
if "changelog" in self.contents:
self.previous_json_file = rougailconfig["doc.previous_json_file"]
if output_format == 'console':
force_true_color_terminal = rougailconfig["doc.force_true_color_terminal"]
else:
force_true_color_terminal = None
self.formater = outputs[output_format](self.with_family, force_true_color_terminal=force_true_color_terminal)
self.informations = None
try:
groups.namespace
self.support_namespace = True
except AttributeError:
self.support_namespace = False
self.property_to_string = get_properties_to_string()
super().__init__()
def run(self) -> str:
"""Print documentation in stdout"""
self.load()
return_string = ""
if "variables" in self.contents:
return_string += self.formater.run(self.informations, self.level)
if "example" in self.contents:
return_string += self.gen_doc_examples()
if "changelog" in self.contents:
return_string += self.gen_doc_changelog()
return True, return_string
def print(self) -> None:
ret, data = self.run()
print(data)
return ret
def load(self):
self.dynamic_paths = {}
config = self.conf.unrestraint
self.populate_dynamics(config=config)
informations = self.parse_families(config)
if informations is None:
informations = {}
self.informations = informations
def populate_dynamics(self, *, config=None, reload=False):
if config is None:
config = self.conf.unrestraint
self._populate_dynamics(config, reload)
def _populate_dynamics(self, family, reload, uncalculated=False) -> None:
def populate(child, uncalculated):
if child.isoptiondescription():
type_ = "family"
else:
type_ = "variable"
if child.isdynamic():
self.populate_dynamic(child, type_, reload, uncalculated)
if child.isoptiondescription():
self._populate_dynamics(child, reload, uncalculated)
for child in family.list(uncalculated=uncalculated):
populate(child, uncalculated)
if not uncalculated:
for child in family.list(uncalculated=True):
if child.isdynamic() and child.path(uncalculated=True) not in self.dynamic_paths:
populate(family, uncalculated=True)
def populate_dynamic(self, obj, type_, reload, uncalculated) -> None:
path = obj.path(uncalculated=True)
if path not in self.dynamic_paths:
new_name = True
description = obj.description(uncalculated=True)
name = obj.name(uncalculated=True)
self.dynamic_paths[path] = {
"names": [],
"identifiers": [],
"path": path,
}
if not obj.information.get("forced_description", False):
self.dynamic_paths[path]["description"] = self._convert_description(
description, obj, type_, its_a_path=False
)
elif obj.isoptiondescription():
self.dynamic_paths[path]["description"] = self._convert_description(
description, obj, type_, its_a_path=True
)
if uncalculated:
return
dynamic_obj = self.dynamic_paths[path]
if reload and obj.identifiers() in dynamic_obj["identifiers"]:
return
dynamic_obj["names"].append(obj.name())
dynamic_obj["identifiers"].append(obj.identifiers())
def parse_families(self, family) -> dict:
informations = {}
leader = None
for child in family.list():
if self.is_inaccessible_user_data(child):
continue
if child.type(only_self=True) == "symlink":
continue
if not child.isoptiondescription():
leader = self.parse_variable(child, leader, informations)
else:
self.parse_family(child, informations)
return informations
def is_inaccessible_user_data(self, child):
"""If family is not accessible in read_write mode (to load user_data),
do not comment this family
"""
properties = child.property.get(uncalculated=True)
for hidden_property in HIDDEN_PROPERTIES:
if hidden_property in properties:
return True
calculation = child.information.get(f"{hidden_property}_calculation", None)
if calculation and calculation.get("type") == "variable":
variable_path, value, condition = calculation["value"]
variable = self.conf.forcepermissive.option(variable_path)
try:
variable.value.get()
except AttributeError:
variable = None
if variable and self.is_inaccessible_user_data(variable):
try:
variable_value = self._get_unmodified_default_value(variable)
except VariableCalculationDependencyError:
pass
else:
if (condition == "when" and value == variable_value) or (condition == "when_not" and value != variable_value):
return True
if not child.isoptiondescription():
for hidden_property in self.disabled_modes:
if hidden_property in properties:
return True
return False
def parse_family(self, family, informations: dict, *, force_injection=False) -> None:
path = family.path(uncalculated=True)
name = family.name(uncalculated=True)
sub_informations = self.parse_families(family)
if not force_injection and not sub_informations:
return
# if self.with_family:
family_informations = self._populate_family(
family,
path,
)
if family_informations is not False:
informations[name] = {
"type": self._get_family_type(family),
"informations": family_informations,
"children": sub_informations,
}
def parse_variable(
self,
variable,
leader: dict,
informations: dict,
*,
only_one=False,
) -> Optional[dict]:
path = variable.path(uncalculated=True)
name = variable.name(uncalculated=True)
potential_leader = None
if variable.isdynamic():
# information is already set
potential_leader = self._parse_variable_dynamic(
variable, leader, name, path, informations, only_one
)
elif variable.isfollower() and variable.index():
self._parse_variable_follower_with_index(
variable, leader, name, informations
)
else:
potential_leader = self.parse_variable_normal(
variable, leader, name, path, informations
)
if potential_leader:
leader = potential_leader
return leader
def parse_variable_normal(
self, variable, leader, name: str, path: str, informations: dict
) -> Optional[dict]:
if variable.isdynamic():
sub_informations = self.dynamic_paths[path]
elif variable.isfollower() and path in informations: # variable.index():
sub_informations = informations[name]
else:
sub_informations = {}
if not self._populate_variable(
variable,
sub_informations,
):
return None
if self.example:
self._add_examples(variable, sub_informations, leader)
informations[name] = sub_informations
if variable.isleader():
return sub_informations
return None
def _parse_variable_follower_with_index(
self, variable, leader: dict, name: str, informations: dict
) -> None:
if not self.example or (variable.index() + 1) > len(leader["example"][-1]):
return
informations[name]["example"][-1][variable.index()] = self._get_example(
variable, informations[name], None
)
def _parse_variable_dynamic(
self, variable, leader, name, path, informations, only_one
) -> None:
# if path not in self.dynamic_paths:
# self.populate_dynamic(variable, path)
dynamic_variable = self.dynamic_paths[path]
if (not only_one or path in informations) and "type" in dynamic_variable:
if self.example:
dynamic_variable["example"].append(
self._get_example(variable, dynamic_variable, leader)
)
if variable.isleader():
return dynamic_variable
if not only_one:
return None
return self.parse_variable_normal(variable, leader, name, path, informations)
def _get_family_type(self, family) -> str:
if self.support_namespace and family.group_type() is groups.namespace:
return "namespace"
if family.isleadership():
return "leadership"
if family.isdynamic(only_self=True):
return "dynamic"
return "family"
def _populate_family(
self,
family,
path: str,
) -> dict:
if family.isdynamic():
informations = self.dynamic_paths[path]
else:
informations = {}
if not self._populate(family, informations, "family"):
return False
if family.isleadership():
informations.setdefault("help", []).append(
_("This family contains lists of variable blocks.")
)
if family.isdynamic(only_self=True):
identifiers = self._to_string(family, "dynamic", do_not_raise=True)
if identifiers is None:
identifiers = family.identifiers(only_self=True)
if not isinstance(identifiers, list):
identifiers = [identifiers]
informations["identifier"] = identifiers
informations.setdefault("help", []).append(
_("This family builds families dynamically.")
)
return informations
def _populate_variable(
self,
variable,
informations: dict,
):
informations["type"] = "variable"
default = self._get_default(
variable,
)
if default is not None:
informations["default"] = {"name": _("Default"), "values": default}
self._parse_type(
variable,
informations,
)
if not self._populate(variable, informations, "variable"):
return False
if variable.ismulti():
multi = not variable.isfollower() or variable.issubmulti()
else:
multi = False
if multi:
informations["properties"].append(
{
"type": "multiple",
"name": _("multiple"),
}
)
examples = variable.information.get("examples", None)
if examples is None:
examples = variable.information.get("test", None)
if examples is not None:
if len(examples) == 1:
name = _("Example")
values = examples[0]
else:
name = _("Examples")
values = list(examples)
informations["examples"] = {
"name": name,
"values": values
}
tags = variable.information.get("tags", None)
if tags:
if len(tags) == 1:
name = _("Tag")
values = tags[0]
else:
name = _("Tags")
values = list(tags)
informations["tags"] = {
"name": name,
"values": values,
}
return True
def _populate(
self,
child,
informations: dict,
type_: str,
):
need_disabled, properties = self._parse_properties(child)
if not need_disabled:
return False
name = child.name(uncalculated=True)
if child.information.get("forced_description", False):
if (
not child.isoptiondescription()
or not self.support_namespace
or child.group_type() is not groups.namespace
):
if (
child.isoptiondescription()
or not child.isfollower()
or not child.index()
):
warning = _('No attribute "description" for "{0}" in {1}').format(
child.path(uncalculated=True),
display_xmlfiles(child.information.get("ymlfiles")),
)
warn(
warning,
RougailWarning,
)
if child.isoptiondescription():
description = self._convert_description(
child.description(uncalculated=True), child, type_, its_a_path=True
)
else:
description = None
else:
description = self._convert_description(
child.description(uncalculated=True), child, type_, its_a_path=False
)
if not child.isdynamic():
informations["path"] = child.path(uncalculated=True)
informations["names"] = [child.name()]
if description is not None:
informations["description"] = description
help_ = child.information.get("help", None)
if help_:
informations["help"] = [to_phrase(help_)]
if "properties" in informations:
informations["properties"].extend(properties)
else:
informations["properties"] = properties
return True
def _convert_description(self, description, obj, type_, its_a_path=False):
if not its_a_path:
description = to_phrase(description, type_)
# if "{{ identifier }}" in description:
# description = {"description": description,
# "identifier": obj.identifiers()[-1],
# }
return description
def _add_examples(self, variable, informations: dict, leader) -> None:
if not variable.index():
example = self._get_example(variable, informations, leader)
informations["example"] = [example]
informations["mandatory_without_value"] = "mandatory" in variable.property.get(
uncalculated=True
) and (
not variable.information.get("default_value_makes_sense", True)
or variable.value.get(uncalculated=True) in [None, []]
)
def _get_example(self, variable, informations: dict, leader):
example = informations.get("examples", {}).get("values")
if example is not None:
if isinstance(example, tuple):
example = list(example)
for prop in informations["properties"]:
if prop["type"] == "multiple":
if not isinstance(example, list):
example = [example]
break
else:
if isinstance(example, list):
index = variable.index()
if index is None or len(example) - 1 >= index:
index = 0
example = example[index]
else:
if variable.information.get("fake_default", False):
default = None
else:
try:
default = variable.value.get()
except ConfigError:
default = None
if default not in [None, []]:
example = default
else:
example = self.get_type_default_value(
variable, informations["properties"]
)
if leader is not None and variable.isfollower():
example = [example] + [undefined] * (len(leader["example"][-1]) - 1)
return example
def get_type_default_value(self, variable, properties):
example = self.convert_option.get(variable.information.get("type"), {}).get(
"example", None
)
if example is None:
example = "xxx"
for prop in properties:
if prop["type"] == "multiple":
multi = True
break
else:
multi = False
if multi:
example = [example]
return example
def _parse_type(
self,
child,
informations,
):
variable_type = child.information.get("type")
doc_type = DocTypes.get(variable_type, {"params": {}})
informations["properties"] = [
{
"type": "type",
"name": doc_type.get("msg", variable_type),
}
]
# extra parameters for types
option = child.get()
validators = []
for param, msg in doc_type["params"].items():
value = option.impl_get_extra(f"_{param}")
if value is None:
value = option.impl_get_extra(param)
if value is not None and value is not False:
if isinstance(value, set):
value = list(value)
if isinstance(value, list):
value = display_list(value, add_quote=True)
validators.append(msg.format(value))
# get validation information from annotator
for name in child.information.list():
if not name.startswith("validators_calculation"):
continue
validators.extend(
self._to_string(
child,
"validators",
)
)
break
if child.information.get("type") == "regexp":
validators.append(
_('text based with regular expressions "{0}"').format(child.pattern())
)
if validators:
if len(validators) == 1:
key = _("Validator")
validators = validators[0]
else:
key = _("Validators")
informations["validators"] = {"name": key, "values": validators}
if child.information.get("type") == "choice":
choices = self._to_string(child, "choice", do_not_raise=True)
if choices is None:
choices = child.value.list()
for idx, val in enumerate(choices):
if isinstance(val, Calculation):
choices[idx] = self._to_string(child, "choice", f"_{idx}")
informations["choices"] = {"name": _("Choices"), "values": choices}
def _parse_properties(
self,
child,
):
informations = []
properties = child.property.get(uncalculated=True)
for mode in self.modes_level:
if mode not in properties:
continue
informations.append(
{
"type": "mode",
"name": mode,
}
)
break
for prop, translated_prop in self.property_to_string:
if prop in properties:
prop_obj = {
"type": "property",
"name": translated_prop,
}
elif child.information.get(f"{prop}_calculation", False):
annotation = self._to_string(child, prop)
if annotation is None or isinstance(annotation, bool):
if annotation is None and prop in HIDDEN_PROPERTIES:
return False, {}
if not annotation:
continue
prop_obj = {
"type": "property",
"name": translated_prop,
}
else:
prop_obj = {
"type": "property",
"name": translated_prop,
"annotation": annotation,
}
else:
# this property is not in the variable so, do not comment it
continue
informations.append(prop_obj)
return True, informations
def _get_default(
self,
variable,
):
default = self._to_string(variable, "default", do_not_raise=True)
if default is not None:
if default == []:
default = None
return default
if variable.information.get("default_value_makes_sense", True):
default_ = variable.value.get(uncalculated=True)
if not isinstance(default_, Calculation):
default = default_
if default == []:
default = None
return default
def _to_string(
self,
child,
prop,
do_not_raise=False,
):
calculation = child.information.get(f"{prop}_calculation", None)
if not calculation:
if do_not_raise:
return None
raise Exception(
f'cannot find "{prop}_calculation" information, '
"do you have declare doc has a plugins?"
)
if isinstance(calculation, list):
values = []
for cal in calculation:
value = self._calculation_to_string(child, cal, prop, inside_list=True)
if value is not None:
values.append(value)
return values
return self._calculation_to_string(child, calculation, prop)
def _calculation_to_string(self, child, calculation, prop, inside_list=False):
if "description" in calculation:
values = calculation["description"]
if not values.endswith("."):
values += "."
return values
if "type" not in calculation:
return calculation["value"]
if calculation["type"] == "jinja":
values = self._calculation_jinja_to_string(child, calculation, prop)
elif calculation["type"] == "variable":
values = self._calculation_variable_to_string(child, calculation, prop)
elif calculation["type"] == "identifier":
if prop in PROPERTY_ATTRIBUTE:
values = calculation["value"]
else:
values = _("the value of the identifier")
elif calculation["type"] == "information":
values = calculation["value"]
else:
values = _("the value of the {0}").format(calculation["type"])
if not inside_list and isinstance(values, str) and not values.endswith("."):
values += "."
return values
def _calculation_jinja_to_string(self, child, calculation, prop):
if calculation["value"] is not True:
values = calculation["value"]
else:
values = _("depends on a calculation")
if (
child.isoptiondescription()
or not child.isfollower()
or not child.index()
):
warning = _(
'"{0}" is a calculation for {1} but has no description in {2}'
).format(
prop,
child.path(),
display_xmlfiles(child.information.get("ymlfiles")),
)
warn(
warning,
RougailWarning,
)
return values
def _calculation_variable_to_string(self, child, calculation, prop):
if prop in PROPERTY_ATTRIBUTE:
variable_path, value, condition = calculation["value"]
variable = self.conf.forcepermissive.option(variable_path)
try:
variable.value.get()
except AttributeError as err:
if prop in HIDDEN_PROPERTIES:
return False
variable = None
if variable and self.is_inaccessible_user_data(variable):
try:
variable_value = self._get_unmodified_default_value(variable)
except VariableCalculationDependencyError:
msg = _("depends on an undocumented variable")
else:
if (
condition == "when"
and value == variable_value
or condition == "when_not"
and value != variable_value
):
if prop in HIDDEN_PROPERTIES:
return
# always "{prop}" (but depends on an undocumented variable)
return True
# depends on an undocumented variable but is never "{prop}"
return False
elif condition == "when_not":
if not calculation["optional"]:
msg = _('when the variable "{0}" hasn\'t the value "{1}"')
else:
msg = _(
'when the variable "{0}" is defined and hasn\'t the value "{1}"'
)
else:
if not calculation["optional"]:
msg = _('when the variable "{0}" has the value "{1}"')
else:
msg = _(
'when the variable "{0}" is defined and has the value "{1}"'
)
if not isinstance(value, str):
value = dump(value)
values = msg.format(variable_path, value)
else:
if calculation["optional"]:
path = calculation["value"]
if "{{ identifier }}" in path:
if path not in self.dynamic_paths:
return None
else:
try:
self.conf.forcepermissive.option(path).get()
except AttributeError:
return None
if not calculation["optional"]:
true_msg = _('the value of the variable "{0}"')
else:
true_msg = _('the value of the variable "{0}" if it is defined')
hidden_msg = _("the value of an undocumented variable")
if "{{ identifier }}" in calculation["ori_path"]:
if calculation["value"] == calculation["ori_path"]:
regexp = None
else:
regexp = compile(
"^"
+ calculation["ori_path"].replace("{{ identifier }}", "(.*)")
+ "$"
)
informations = [self.dynamic_paths[calculation["value"]]]
values = []
all_is_undocumented = None
for information in informations:
# if calculation["ori_path"] == information['path']:
path = information["path"]
for identifiers in information["identifiers"]:
cpath = calc_path(path, identifiers=identifiers)
if regexp and not regexp.search(cpath):
continue
if self.is_inaccessible_user_data(self.conf.option(cpath)):
if all_is_undocumented is None:
all_is_undocumented = True
msg = hidden_msg
else:
if "{{ identifier }}" in path:
msg = {
"message": true_msg,
"path": {
"path": path,
"identifiers": identifiers.copy(),
},
}
else:
msg = true_msg.format(path)
all_is_undocumented = False
values.append(msg)
if all_is_undocumented and len(values) > 1:
values = _("the values of undocumented variables")
else:
variable_path = calculation["ori_path"]
variable = self.conf.forcepermissive.option(variable_path)
try:
isfollower = variable.isfollower()
except AttributeError as err:
pass
else:
if not isfollower and self.is_inaccessible_user_data(variable):
try:
uncalculated = variable.value.get(uncalculated=True)
except PropertiesOptionError:
true_msg = None
else:
if uncalculated and not isinstance(
uncalculated, Calculation
):
if isinstance(uncalculated, list):
true_msg = {
"submessage": _(
"(from an undocumented variable)"
),
"values": uncalculated,
}
else:
if not isinstance(uncalculated, str):
uncalculated = dump(uncalculated)
true_msg = _(
"{0} (from an undocumented variable)"
).format(uncalculated)
else:
true_msg = _("depends on an undocumented variable")
if true_msg:
if isinstance(true_msg, dict):
values = true_msg
else:
values = true_msg.format(calculation["ori_path"])
else:
values = None
return values
def _get_unmodified_default_value(self, child):
calculation = child.information.get(f"default_calculation", None)
if not calculation:
return child.value.get()
if calculation["type"] == "variable":
variable = self.conf.forcepermissive.option(calculation["value"])
if variable and self.is_inaccessible_user_data(variable):
return self._get_unmodified_default_value(variable)
raise VariableCalculationDependencyError()

View file

@ -1,167 +0,0 @@
"""
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 typing import Optional
from .utils import _, calc_path
class Examples: # pylint: disable=no-member,too-few-public-methods
"""Build examples"""
def __init__(self):
self.examples = None
self.examples_mandatories = None
def gen_doc_examples(self):
"""Return examples"""
self._build_examples()
return_string = ""
if self.examples_mandatories:
return_string += self.formater.title(
_("Example with mandatory variables not filled in"), self.level
)
return_string += self.formater.yaml(self.examples_mandatories)
if self.examples:
return_string += self.formater.title(
_("Example with all variables modifiable"), self.level
)
return_string += self.formater.yaml(self.examples)
return return_string
def _build_examples(self):
self.examples, self.examples_mandatories = self._parse_examples(
self.informations
)
def _parse_examples(self, dico, dyn_parent: Optional[str] = None) -> tuple:
examples = {}
examples_mandatories = {}
for value in dico.values():
if value["type"] == "variable":
self._parse_examples_variable(
value, dyn_parent, examples, examples_mandatories
)
else:
self._parse_examples_family(
value, dyn_parent, examples, examples_mandatories
)
return examples, examples_mandatories
def _parse_examples_variable(
self,
variable,
dyn_parent: Optional[str],
examples: dict,
examples_mandatories: dict,
) -> None:
paths = []
ori_path = variable["path"]
if "identifiers" in variable:
for idx, identifiers in enumerate(variable["identifiers"]):
paths.append(calc_path(ori_path, identifiers=identifiers))
else:
paths.append(ori_path)
for idx, path in enumerate(paths):
path = calc_path(path)
if dyn_parent is not None and not path.startswith(dyn_parent):
continue
if len(variable["names"]) == 1:
name = variable["names"][0]
else:
name = variable["names"][idx]
value = variable["example"][idx]
examples[name] = value
if variable["mandatory_without_value"]:
examples_mandatories[name] = value
break
def _parse_examples_family(
self,
family,
dyn_parent: Optional[str],
examples: dict,
examples_mandatories: dict,
) -> None:
def _set_example(idx, identifiers):
path = calc_path(ori_path, identifiers=identifiers)
if dyn_parent is not None and not path.startswith(dyn_parent):
return
if len(family["informations"]["names"]) == 1:
name = family["informations"]["names"][0]
else:
name = family["informations"]["names"][idx]
if family["type"] == "leadership":
func = self._parse_examples_leadership
else:
func = self._parse_examples
ret_e, ret_m = func(
family["children"],
path + "." if family["type"] == "dynamic" else dyn_parent,
)
if ret_m:
examples_mandatories[name] = ret_m
if ret_e:
examples[name] = ret_e
ori_path = family["informations"]["path"]
if "identifiers" in family["informations"]:
for idx, identifiers in enumerate(family["informations"]["identifiers"]):
_set_example(idx, identifiers)
else:
_set_example(0, None)
def _parse_examples_leadership(
self, leadership, dyn_parent: Optional[str] = None
) -> tuple:
examples = []
examples_mandatories = []
leader = next(iter(leadership.values()))
paths = []
ori_path = leader["path"]
if "identifiers" in leader:
for idx, identifiers in enumerate(leader["identifiers"]):
paths.append(calc_path(ori_path, identifiers=identifiers))
else:
paths.append(ori_path)
for path_idx, path in enumerate(paths):
path = calc_path(path)
if dyn_parent is not None and not path.startswith(dyn_parent):
continue
for leader_idx in range(len(leader["example"][path_idx])):
followers = {}
for follower in leadership.values():
if len(follower["names"]) == 1:
name = follower["names"][0]
else:
name = follower["names"][path_idx]
followers[name] = follower["example"][path_idx][leader_idx]
examples.append(followers)
if leader["mandatory_without_value"]:
followers = {}
for follower in leadership.values():
if not follower["mandatory_without_value"]:
continue
if len(follower["names"]) == 1:
name = follower["names"][0]
else:
name = follower["names"][path_idx]
followers[name] = follower["example"][path_idx][leader_idx]
examples_mandatories.append(followers)
break
return examples, examples_mandatories

View file

@ -1,6 +1,6 @@
"""Internationalisation utilities """Internationalisation utilities
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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
@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from gettext import translation from gettext import translation
from pathlib import Path from pathlib import Path
t = translation( t = translation("rougail_output_doc", str(Path(__file__).parent / "locale"), fallback=True)
"rougail_output_doc", str(Path(__file__).parent / "locale"), fallback=True
)
_ = t.gettext _ = t.gettext

View file

@ -1,6 +1,6 @@
"""Loads output """Loads output
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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,6 +1,6 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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,36 +16,86 @@ 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 io import BytesIO
from typing import List from typing import List
from ..utils import CommonFormater, dump from itertools import chain
from ruamel.yaml import YAML
class Formater(CommonFormater): class Formater:
"""The asciidoc formater"""
name = "asciidoc" name = "asciidoc"
_table_name = "asciidoc"
level = 40 level = 40
def __init__(self):
self._yaml = YAML()
self._yaml.indent(mapping=2, sequence=4, offset=2)
def header(self):
return ""
def title( def title(
self, self,
title: str, title: str,
level: int, level: int,
) -> str: ) -> str:
"""Display family name as a title"""
char = "=" char = "="
return f"{char * (level + 1)} {title}\n\n" return f"{char * (level + 1)} {title}\n\n"
def yaml(self, dump: dict) -> str:
return f"[,yaml]\n----\n{self.dump(dump)}\n----\n"
def table(self, table: str) -> str:
# add 'a' option in cols to display list
stable = table.split("\n", 1)
return stable[0].replace("<", "a") + "\n" + stable[1]
def link(
self,
comment: str,
link: str,
) -> str:
return f"`{link}[{comment}]`"
def prop(
self,
prop: str,
) -> str:
return f"`{prop}`"
def list(
self,
choices: list,
) -> str:
prefix = "\n\n* "
char = "\n* "
return prefix + char.join([self.dump(choice) for choice in choices])
def is_list(
self,
txt: str,
) -> str:
return txt.startswith("* ")
def columns(
self,
col1: List[str],
col2: List[str],
) -> None:
self.max_line = 0
for params in chain(col1, col2):
for param in params.split("\n"):
self.max_line = max(self.max_line, len(param))
self.max_line += 1
def join( def join(
self, self,
lst: List[str], lst: List[str],
) -> str: ) -> str:
"""Display line in table from a list"""
string = "" string = ""
previous = "" previous = ""
for line in lst: for line in lst:
if string: if string:
if self.is_list(previous.split("\n", 1)[-1]): if self.is_list(previous.split("\n")[-1]):
string += "\n\n" string += "\n\n"
else: else:
string += " +\n" string += " +\n"
@ -54,83 +104,36 @@ class Formater(CommonFormater):
previous = line previous = line
return "\n" + string return "\n" + string
def to_string(
self,
text: str,
) -> str:
return text
def table_header(
self,
lst,
):
return lst[0] + " " * (self.max_line - len(lst[0])), lst[1] + " " * (
self.max_line - len(lst[1])
)
def bold( def bold(
self, self,
msg: str, msg: str,
) -> str: ) -> str:
"""Set a text to bold"""
return f"**{msg}**" return f"**{msg}**"
def italic( def italic(
self, self,
msg: str, msg: str,
) -> str: ) -> str:
"""Set a text to italic""" return f"_{msg}_"
return f"__{msg}__"
def delete( def dump(self, dico):
self, with BytesIO() as ymlfh:
msg: str, self._yaml.dump(dico, ymlfh)
) -> str: ret = ymlfh.getvalue().decode("utf-8").strip()
"""Set a text to deleted""" if ret.endswith("..."):
return f"+++{msg}+++" ret = ret[:-3].strip()
return ret
def underline(
self,
msg: str,
) -> str:
"""Set a text to underline"""
return f"#{msg}#"
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
return text.strip()
def list(
self,
choices: list,
) -> str:
"""Display a liste of element"""
prefix = "\n\n* "
char = "\n* "
return prefix + char.join([dump(choice) for choice in choices])
def prop(
self,
prop: str,
italic: bool,
) -> str:
"""Display property"""
if italic:
prop = self.italic(prop)
return f"`{prop}`"
def yaml(self, _dump: dict) -> str:
"""Dump yaml part of documentation"""
return f"[,yaml]\n----\n---\n{dump(_dump)}\n----\n"
def table(self, datas: list, with_header: bool = True) -> str:
"""Transform list to a table in string format
we change the first line because we want that col has the same width
"""
table = super().table(datas, with_header)
stable = table.split("\n", 1)
return '[cols="1a,1a"]\n' + stable[1]
def link(
self,
comment: str,
link: str,
) -> str:
"""Add a link"""
return f"`{link}[{comment}]`"
def is_list(
self,
txt: str,
) -> str:
"""verify if a text is a list"""
return txt.strip().startswith("* ")

View file

@ -1,168 +0,0 @@
"""
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 typing import List
from ..i18n import _
from ..utils import dump, CommonFormater, ENTER
class Formater(CommonFormater):
"""The markdown (for github) formater"""
name = "console"
level = 10
enter_table = "\n"
titles_color = {
"title1": "bright_cyan underline bold",
"title2": "bright_green underline bold",
"title3": "green1 underline bold",
"title4": "green3 underline bold",
"title5": "dark_green underline bold",
}
def __init__(self, with_family: bool, force_true_color_terminal: bool=False) -> None:
from rich.table import Table
from rich.theme import Theme
from rich.console import Console
self.rich_table = Table
self.rich_console = Console
if force_true_color_terminal:
self.force_terminal = 'xterm-256color'
else:
self.force_terminal = None
self.custom_theme = Theme(self.titles_color)
self.max_line = 0
super().__init__(with_family)
def run(self, dico: dict, level: int, *, dico_is_already_treated=False) -> str:
if not dico_is_already_treated:
dico = self.dict_to_dict(dico, level)
console = self.rich_console(theme=self.custom_theme, force_terminal=self.force_terminal)
with console.capture() as capture:
for data in dico:
console.print(data)
return capture.get()
def title(
self,
title: str,
level: int,
) -> str:
"""Display family name as a title"""
space = " " * (2 * (level - 1))
return f"{ENTER}{space}[title{level}]{title}[/title{level}]{ENTER}"
def join(
self,
lst: List[str],
) -> str:
"""Display line in table from a list"""
return self.enter_table.join(lst)
def bold(
self,
msg: str,
) -> str:
"""Set a text to bold"""
return f"[bold]{msg}[/bold]"
def italic(
self,
msg: str,
) -> str:
"""Set a text to italic"""
return f"[italic]{msg}[/italic]"
def delete(
self,
msg: str,
) -> str:
"""Set a text to delete"""
return f"[strike]{msg}[/strike]"
def underline(
self,
msg: str,
) -> str:
"""Set a text to underline"""
return f"[underline]{msg}[/underline]"
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
return text
def list(
self,
choices,
) -> str:
"""Display a liste of element"""
char = f"{self.enter_table}- "
ret = ""
for choice in choices:
if not isinstance(choice, str):
choice = dump(choice)
ret += char + choice
return ret
def prop(
self,
prop: str,
italic: bool,
) -> str:
"""Display property"""
prop = f"[reverse][bold] {prop} [/bold][/reverse]"
if italic:
prop = self.italic(prop)
return prop
def yaml(self, _dump):
"""Dump yaml part of documentation"""
return f"```yaml\n---\n{dump(_dump)}\n```\n"
def link(
self,
comment: str,
link: str,
) -> str:
"""Add a link"""
return self.prop(comment, False)
# return f"{comment} ({link})"
def columns(
self,
col: List[str],
) -> None:
"""count columns length"""
for line in col:
for l in line.split(self.enter_table):
self.max_line = max(self.max_line, len(l) + 1)
def table(self, datas: list, with_header: bool = True) -> str:
"""Transform list to a table in string format"""
table = self.rich_table(show_lines=True)
if with_header:
table.add_column(_("Variable"), width=self.max_line)
table.add_column(_("Description"), width=self.max_line)
for data in datas:
table.add_row(str(data[0]), data[1])
return table

View file

@ -1,6 +1,6 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2024-2025 Copyright (C) 2024
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,127 +16,112 @@ 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 io import BytesIO
from typing import List from typing import List
from html import escape from itertools import chain
from ruamel.yaml import YAML
from ..utils import dump, CommonFormater
class Formater(CommonFormater): class Formater:
"""The markdown (for github) formater"""
name = "github" name = "github"
_table_name = "github"
level = 50 level = 50
enter_table = "<br/>"
def __init__(self, with_family: bool, **kwarg) -> None: def __init__(self):
self.max_line_variable = 0 self._yaml = YAML()
self.max_line_description = 0 self._yaml.indent(mapping=2, sequence=4, offset=2)
super().__init__(with_family) self.header_setted = False
def header(self):
if self.header_setted:
return ""
self.header_setted = True
return "---\ngitea: none\ninclude_toc: true\n---\n"
def title( def title(
self, self,
title: str, title: str,
level: int, level: int,
) -> str: ) -> str:
"""Display family name as a title"""
char = "#" char = "#"
return f"{char * level} {title}\n\n" return f"{char * level} {title}\n\n"
def join( def yaml(self, dump):
self, return f"```yaml\n---\n{self.dump(dump)}\n```\n"
lst: List[str],
) -> str:
"""Display line in table from a list"""
return self.enter_table.join(lst)
def bold( def table(self, table):
self, return table
msg: str,
) -> str:
"""Set a text to bold"""
return f"**{msg}**"
def italic(
self,
msg: str,
) -> str:
"""Set a text to italic"""
return f"*{msg}*"
def delete(
self,
msg: str,
) -> str:
"""Set a text to deleted"""
return f"~~{msg}~~"
def underline(
self,
msg: str,
) -> str:
"""Set a text to underline"""
return f"<ins>{msg}</ins>"
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
return text.strip().replace("\n", self.enter_table)
def list(
self,
choices,
):
"""Display a liste of element"""
char = f"{self.enter_table}- "
ret = ""
for choice in choices:
if not isinstance(choice, str):
choice = dump(choice)
ret += char + choice
return ret
def prop(
self,
prop: str,
italic: bool,
) -> str:
"""Display property"""
prop = f"`{prop}`"
if italic:
prop = self.italic(prop)
return prop
def table_header(self, lst):
"""Manage the header of a table"""
return lst[0] + "&nbsp;" * (self.max_line_variable - len(lst[0])), lst[
1
] + "&nbsp;" * (self.max_line_description - len(lst[1]))
def yaml(self, _dump):
"""Dump yaml part of documentation"""
return f"```yaml\n---\n{dump(_dump)}\n```\n"
def link( def link(
self, self,
comment: str, comment: str,
link: str, link: str,
) -> str: ) -> str:
"""Add a link"""
return f"[`{comment}`]({link})" return f"[`{comment}`]({link})"
def prop(
self,
prop: str,
) -> str:
return f"`{prop}`"
def list(
self,
choices,
):
prefix = "<br/>- "
char = "<br/>- "
return prefix + char.join([self.dump(choice) for choice in choices])
def is_list(
self,
txt: str,
) -> str:
return txt.startswith("* ")
def columns( def columns(
self, self,
col: List[str], col1: List[str],
col2: List[str],
) -> None: ) -> None:
"""count columns length""" self.max_line = 0
for line in col: for params in chain(col1, col2):
for l in line.split(self.enter_table): for param in params.split("\n"):
self.max_line_variable = max(self.max_line_variable, len(l) + 1) self.max_line = max(self.max_line, len(param))
self.max_line_description = self.max_line_variable self.max_line += 1
def to_phrase(self, text: str) -> str: def join(
return escape(text) self,
lst: List[str],
) -> str:
return "<br/>".join(lst)
def to_string(
self,
text: str,
) -> str:
return text.strip().replace("\n", "<br/>")
def table_header(self, lst):
return lst[0] + "&nbsp;" * (self.max_line - len(lst[0])), lst[1] + "&nbsp;" * (
self.max_line - len(lst[1])
)
def bold(
self,
msg: str,
) -> str:
return f"**{msg}**"
def italic(
self,
msg: str,
) -> str:
return f"*{msg}*"
def dump(self, dico):
with BytesIO() as ymlfh:
self._yaml.dump(dico, ymlfh)
ret = ymlfh.getvalue().decode("utf-8").strip()
if ret.endswith("..."):
ret = ret[:-3].strip()
return ret

View file

@ -1,64 +0,0 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 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 List
from .github import Formater as GithubFormater
from ..i18n import _
class Formater(GithubFormater):
name = "gitlab"
level = 51
def namespace_to_title(self, informations: dict, level: int) -> str:
"""manage namespace family"""
return self.title(
self.get_description("family", informations, {}, None),
level,
)
def title(self, title: str, level: int) -> str:
# self.max_line_variable = 0
return "<details><summary>" + title + "</summary>\n\n"
def end_family(self, level):
return "</details>\n\n"
def columns(
self,
col: List[str],
) -> None:
pass
def family_informations(self) -> str:
return f">>> [!note] {_('Informations')}\n"
def end_family_informations(self) -> str:
return f"\n>>>\n"
def after_family_paths(self) -> str:
return "<br>"
def after_family_properties(self) -> str:
return ""
def table_header(self, lst):
"""Manage the header of a table"""
return lst

View file

@ -1,135 +0,0 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 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 List
from html import escape
from ..utils import CommonFormater, dump
class Formater(CommonFormater):
"""The asciidoc formater"""
name = "html"
_table_name = "unsafehtml"
level = 45
def title(
self,
title: str,
level: int,
) -> str:
"""Display family name as a title"""
return f"<h{level}>{title}</h{level}>\n\n"
def join(
self,
lst: List[str],
) -> str:
"""Display line in table from a list"""
string = ""
previous = ""
for line in lst:
if string:
# if self.is_list(previous.split("\n", 1)[-1]):
# string += "<br/><br/>"
# else:
string += "<br/>"
string += line
previous = line
return string
def bold(
self,
msg: str,
) -> str:
"""Set a text to bold"""
return f"<b>{msg}</b>"
def italic(
self,
msg: str,
) -> str:
"""Set a text to italic"""
return f"<i>{msg}</i>"
def delete(
self,
msg: str,
) -> str:
"""Set a text to deleted"""
return f"<del>{msg}</del>"
def underline(
self,
msg: str,
) -> str:
"""Set a text to underline"""
return f"<ins>{msg}</ins>"
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
return text.strip()
def list(
self,
choices: list,
) -> str:
"""Display a liste of element"""
prefix = "<ul>"
char = "\n"
return (
"<ul>"
+ char.join(["<li>" + dump(choice) + "</li>" for choice in choices])
+ "</ul>"
)
def prop(
self,
prop: str,
italic: bool,
) -> str:
"""Display property"""
if italic:
prop = self.italic(prop)
return f"<mark>{prop}</mark>"
def yaml(self, _dump: dict) -> str:
"""Dump yaml part of documentation"""
return f"<pre>{dump(_dump)}</pre>"
def link(
self,
comment: str,
link: str,
) -> str:
"""Add a link"""
return self.prop(f"<a href='{link}'>{comment}</a>", False)
def is_list(
self,
txt: str,
) -> str:
"""verify if a text is a list"""
return txt.strip().startswith("<ul>")
def to_phrase(self, text: str) -> str:
return escape(text)

View file

@ -1,34 +0,0 @@
"""
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 json import dumps
from typing import Any
class Formater:
"""Just return internal structure to json"""
name = "json"
level = 90
def __init__(self, with_family: bool, **kwarg):
pass
def run(self, dico: dict, *args) -> str: # pylint: disable=unused-argument
"""Transform to string"""
return dumps(dico, ensure_ascii=False, indent=2)

View file

@ -1,852 +0,0 @@
"""
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 typing import Tuple, List, Optional
from io import BytesIO
from ruamel.yaml import YAML
import tabulate as tabulate_module
from tiramisu.error import display_list
from tabulate import tabulate
from rougail.tiramisu import normalize_family
from tiramisu import undefined
from .i18n import _
ROUGAIL_VARIABLE_TYPE = (
"https://rougail.readthedocs.io/en/latest/variable.html#variables-types"
)
ENTER = "\n\n"
DocTypes = {
"domainname": {
"params": {
"allow_startswith_dot": _("the domain name can starts by a dot"),
"allow_without_dot": _("the domain name can be a hostname"),
"allow_ip": _("the domain name can be an IP"),
"allow_cidr_network": _("the domain name can be network in CIDR format"),
},
},
"number": {
"params": {
"min_number": _("the minimum value is {0}"),
"max_number": _("the maximum value is {0}"),
},
},
"integer": {
"params": {
"min_integer": _("the minimum value is {0}"),
"max_integer": _("the maximum value is {0}"),
},
},
"ip": {
"msg": "IP",
"params": {
"cidr": _("IP must be in CIDR format"),
"private_only": _("private IP are allowed"),
"allow_reserved": _("reserved IP are allowed"),
},
},
"network": {
"params": {
"cidr": _("network must be in CIDR format"),
},
},
"hostname": {
"params": {
"allow_ip": _("the host name can be an IP"),
},
},
"web_address": {
"params": {
"allow_ip": _("the domain name in web address can be an IP"),
"allow_without_dot": _(
"the domain name in web address can be only a hostname"
),
},
},
"port": {
"params": {
"allow_range": _("can be range of port"),
"allow_protocol": _("can have the protocol"),
"allow_zero": _("port 0 is allowed"),
"allow_wellknown": _("well-known ports (1 to 1023) are allowed"),
"allow_registred": _("registred ports (1024 to 49151) are allowed"),
"allow_private": _("private ports (greater than 49152) are allowed"),
},
},
"secret": {
"params": {
"min_len": _("minimum length for the secret is {0} characters"),
"max_len": _("maximum length for the secret is {0} characters"),
"forbidden_char": _("forbidden characters: {0}"),
},
},
"unix_filename": {
"params": {
"allow_relative": _("this filename could be a relative path"),
"test_existence": _("this file must exists"),
"types": _("file type allowed: {0}"),
},
},
}
_yaml = YAML()
_yaml.indent(mapping=2, sequence=4, offset=2)
def dump(informations):
"""Dump variable, means transform bool, ... to yaml string"""
with BytesIO() as ymlfh:
_yaml.dump(informations, ymlfh)
ret = ymlfh.getvalue().decode("utf-8").strip()
if ret.endswith("..."):
ret = ret[:-3].strip()
return ret
def to_phrase(msg, type_="variable"):
"""Add maj for the first character and ends with dot"""
if not msg:
# replace None to empty string
return ""
msg = str(msg).strip()
# a phrase must ends with a dot
if type_ == "variable":
if not msg.endswith("."):
msg += "."
elif type_ == "family":
if msg.endswith("."):
msg = msg[:-1]
else:
raise Exception("unknown type")
# and start with a maj
return msg[0].upper() + msg[1:]
class CommonFormater:
"""Class with common function for formater"""
enter_table = "\n"
# tabulate module name
name = None
def __init__(self, with_family: bool, **kwarg):
tabulate_module.PRESERVE_WHITESPACE = True
self.header_setted = False
self.with_family = with_family
# Class you needs implement to your Formater
def title(
self,
title: str,
level: int,
) -> str:
"""Display family name as a title"""
raise NotImplementedError()
def join(
self,
lst: List[str],
) -> str:
"""Display line in table from a list"""
raise NotImplementedError()
def bold(
self,
msg: str,
) -> str:
"""Set a text to bold"""
raise NotImplementedError()
def stripped(
self,
text: str,
) -> str:
"""Return stripped text (as help)"""
raise NotImplementedError()
def list(
self,
choices: list,
) -> str:
"""Display a liste of element"""
raise NotImplementedError()
def prop(
self,
prop: str,
italic: bool,
) -> str:
"""Display property"""
raise NotImplementedError()
def link(
self,
comment: str,
link: str,
) -> str:
"""Add a link"""
raise NotImplementedError()
##################
def family_informations(self) -> str:
return ""
def end_family_informations(self) -> str:
return ""
def display_paths(
self,
informations: dict,
modified_attributes: dict,
force_identifiers: Optional[str],
) -> str:
ret_paths = []
path = informations["path"]
if "identifiers" in modified_attributes:
name, previous, new = modified_attributes["identifiers"]
ret_paths.extend(
[
self.bold(self.delete(calc_path(path, self, identifier)))
for identifier in previous
]
)
else:
new = []
if "identifiers" in informations:
for identifier in informations["identifiers"]:
if force_identifiers and identifier != force_identifiers:
continue
path_ = calc_path(path, self, identifier)
if identifier in new:
path_ = self.underline(path_)
ret_paths.append(self.bold(path_))
else:
ret_paths.append(self.bold(path))
return ret_paths
def after_family_paths(self) -> str:
return ENTER
def after_family_properties(self) -> str:
return ENTER
def table_header(
self,
lst: list,
) -> tuple:
"""Manage the header of a table"""
return lst
def run(
self, informations: dict, level: int, *, dico_is_already_treated=False
) -> str:
"""Transform to string"""
if informations:
return self._run(informations, level, dico_is_already_treated)
return ""
def _run(self, dico: dict, level: int, dico_is_already_treated: bool) -> str:
"""Parse the dict to transform to dict"""
if dico_is_already_treated:
return "".join(dico)
return "".join([msg for msg in self.dict_to_dict(dico, level, init=True)])
def dict_to_dict(
self,
dico: dict,
level: int,
*,
ori_table_datas: list = None,
init: bool = False,
) -> str:
"""Parse the dict to transform to dict"""
msg = []
if ori_table_datas is not None:
table_datas = ori_table_datas
else:
table_datas = []
ori_level = None
for value in dico.values():
if value["type"] == "variable":
self.variable_to_string(value, table_datas)
else:
if self.with_family:
if value["type"] == "namespace":
if ori_level is None:
ori_level = level
level += 1
informations = value["informations"]
msg.append(self.namespace_to_title(informations, ori_level))
msg.append(self.family_informations())
msg.extend(self.display_paths(informations, {}, None))
msg.append(self.after_family_paths())
msg.append(
self.property_to_string(informations, {}, {})[1] + ENTER
)
msg.append(self.end_family_informations())
msg.extend(self.dict_to_dict(value["children"], level))
msg.append(self.end_namespace(ori_level))
else:
if table_datas:
msg.append(self.table(table_datas))
table_datas = []
msg.extend(self.family_to_string(value["informations"], level))
msg.extend(self.dict_to_dict(value["children"], level + 1))
msg.append(self.end_family(level))
else:
self.dict_to_dict(
value["children"], level + 1, ori_table_datas=table_datas
)
if (init or ori_table_datas is None) and table_datas:
msg.append(self.table(table_datas))
return msg
# FAMILY
def namespace_to_title(self, informations: dict, level: int) -> str:
"""manage namespace family"""
return self.title(
_('Variables for "{0}"').format(
self.get_description("family", informations, {}, None)
),
level,
)
def end_namespace(self, level: int) -> str:
return self.end_family(level)
def family_to_string(self, informations: dict, level: int) -> str:
"""manage other family type"""
msg = [self.title(self.get_description("family", informations, {}, None), level)]
helps = informations.get("help")
if helps:
for help_ in helps:
msg.append(self.display_family_help(help_.strip()))
msg.append(self.family_informations())
msg.append(
self.join(self.display_paths(informations, {}, None)) + self.after_family_paths()
)
calculated_properties = []
msg.append(
self.property_to_string(informations, calculated_properties, {})[1] + ENTER
)
if calculated_properties:
msg.append(
self.join(calculated_properties) + self.after_family_properties()
)
if "identifier" in informations:
msg.append(
self.section(_("Identifiers"), informations["identifier"])
+ self.after_family_properties()
)
msg.append(self.end_family_informations())
return msg
def end_family(self, level: int) -> str:
return ""
def convert_list_to_string(
self, attribute: str, informations: dict, modified_attributes: dict
) -> str():
datas = []
if attribute in modified_attributes:
name, previous, new = modified_attributes[attribute]
for data in previous:
datas.append(self.delete(self.to_phrase(data)))
else:
new = []
if attribute in informations:
for data in informations[attribute]:
if isinstance(data, dict):
if attribute.endswith("s"):
attr = attribute[:-1]
else:
attr = attribute
data = data[attr].replace(
"{{ identifier }}", self.italic(data["identifier"])
)
data = self.to_phrase(data)
if data in new:
data = self.underline(data)
datas.append(data)
return self.stripped(self.join(datas))
def get_description(
self, type_: str, informations: dict, modified_attributes: dict, force_identifiers: Optional[str]
) -> str():
def _get_description(description, identifiers, delete=False, new=[]):
if "{{ identifier }}" in description:
if type_ == "variable":
identifiers_text = display_list(
[self.italic(i[-1]) for i in identifiers if not force_identifiers or i == force_identifiers], separator="or"
)
description = description.replace(
"{{ identifier }}", identifiers_text
)
else:
d = []
for i in identifiers:
if force_identifiers and i != force_identifiers:
continue
new_description = description.replace(
"{{ identifier }}", self.italic(i[-1])
)
if new_description not in d:
d.append(self.to_phrase(new_description))
description = display_list(d, separator="or")
else:
description = self.to_phrase(description)
if description in new:
description = self.underline(description)
if delete:
description = self.delete(description)
return description
if "description" in modified_attributes:
name, previous, new = modified_attributes["description"]
modified_description = _get_description(
previous, modified_attributes.get("identifiers", []), delete=True
)
else:
modified_description = None
new = []
description = _get_description(
informations["description"], informations.get("identifiers"), new=new
)
if modified_description:
if description:
description = self.join([modified_description, description])
else:
description = modified_description
if not description:
return None
return self.stripped(description)
# VARIABLE
def variable_to_string(
self, informations: dict, table_datas: list, modified_attributes: dict = {}, force_identifiers: Optional[str]=None
) -> None:
"""Manage variable"""
calculated_properties = []
multi, first_column = self.variable_first_column(
informations, calculated_properties, modified_attributes, force_identifiers
)
table_datas.append(
[
self.join(first_column),
self.join(
self.variable_second_column(
informations,
calculated_properties,
modified_attributes,
multi,
force_identifiers,
)
),
]
)
def variable_first_column(
self,
informations: dict,
calculated_properties: list,
modified_attributes: Optional[dict],
force_identifiers: Optional[str],
) -> list:
"""Collect string for the first column"""
multi, properties = self.property_to_string(
informations, calculated_properties, modified_attributes
)
first_col = [
self.join(self.display_paths(informations, modified_attributes, force_identifiers)),
properties,
]
self.columns(first_col)
return multi, first_col
def variable_second_column(
self,
informations: dict,
calculated_properties: list,
modified_attributes: dict,
multi: bool,
force_identifiers: Optional[str],
) -> list:
"""Collect string for the second column"""
second_col = []
#
if "description" in informations:
description = self.get_description(
"variable", informations, modified_attributes, force_identifiers,
)
second_col.append(description)
#
help_ = self.convert_list_to_string("help", informations, modified_attributes)
if help_:
second_col.append(help_)
#
validators = self.convert_section_to_string(
"validators", informations, modified_attributes, multi=True
)
if validators:
second_col.append(validators)
default_is_already_set, choices = self.convert_choices_to_string(
informations, modified_attributes
)
if choices:
second_col.append(choices)
if not default_is_already_set and "default" in informations:
self.convert_section_to_string(
"default", informations, modified_attributes, multi=multi
)
second_col.append(
self.convert_section_to_string(
"default", informations, modified_attributes, multi=multi
)
)
examples = self.convert_section_to_string(
"examples", informations, modified_attributes, multi=True
)
if examples:
second_col.append(examples)
tags = self.convert_section_to_string(
"tags", informations, modified_attributes, multi=True
)
if tags:
second_col.append(tags)
second_col.extend(calculated_properties)
self.columns(second_col)
return second_col
def convert_section_to_string(
self, attribute: str, informations: dict, modified_attributes: dict, multi: bool
) -> str():
values = []
submessage = ""
if modified_attributes and attribute in modified_attributes:
name, previous, new = modified_attributes[attribute]
# if "identifiers" in modified_attributes:
# iname, iprevious, inew = modified_attributes["identifiers"]
# identifiers = iprevious.copy()
# for identifier in informations.get("identifiers", []):
# if identifier not in inew:
# identifiers.append(identifier)
#
# else:
# identifiers = informations.get("identifiers", [])
if isinstance(previous, list):
for p in previous:
submessage, m = self.message_to_string(p, submessage)
values.append(self.delete(m))
else:
submessage, old_values = self.message_to_string(previous, submessage)
values.append(self.delete(old_values))
else:
new = []
if attribute in informations:
old = informations[attribute]
name = old["name"]
if isinstance(old["values"], list):
for value in old["values"]:
submessage, old_value = self.message_to_string(value, submessage)
if value in new:
old_value = self.underline(old_value)
values.append(old_value)
if multi:
values = self.list(values)
else:
values = self.join(values)
elif values:
old_values = old["values"]
submessage, old_values = self.message_to_string(old_values, submessage)
if old["values"] in new:
old_values = self.underline(old_values)
values.append(old_values)
values = self.join(values)
else:
submessage, values = self.message_to_string(old["values"], submessage)
if old["values"] in new:
values = self.underline(values)
if values != []:
return self.section(name, values, submessage=submessage)
def convert_choices_to_string(
self, informations: dict, modified_attributes: dict
) -> str():
default_is_already_set = False
if "choices" in informations:
choices = informations["choices"]
choices_values = choices["values"]
if not isinstance(choices_values, list):
choices_values = [choices_values]
default_is_a_list = False
else:
default_is_a_list = True
if "default" in modified_attributes:
name, old_default, new_default = modified_attributes["default"]
if not old_default:
old_default = [None]
if not isinstance(old_default, list):
old_default = [old_default]
for value in old_default.copy():
if (
isinstance(value, str)
and value.endswith(".")
and value not in choices_values
):
old_default.remove(value)
old_default.append(value[:-1])
else:
old_default = new_default = []
# check if all default values are in choices (could be from a calculation)
if "default" in informations:
default = informations["default"]["values"]
else:
default = []
if not isinstance(default, list):
default = [default]
default_value_not_in_choices = set(default) - set(choices_values)
if default_value_not_in_choices:
default_is_changed = False
for val in default_value_not_in_choices.copy():
if (
isinstance(val, str)
and val.endswith(".")
and val[:-1] in choices_values
):
default.remove(val)
default.append(val[:-1])
default_is_changed = True
if val in new_default:
new_default.remove(val)
new_default.append(val[:-1])
if default_is_changed:
default_value_not_in_choices = set(default) - set(choices_values)
if default_value_not_in_choices:
old_default = []
new_default = []
default = []
else:
default_is_already_set = True
if "choices" in modified_attributes:
name, previous, new = modified_attributes["choices"]
for choice in reversed(previous):
if choice in old_default:
choices_values.insert(
0, self.delete(dump(choice) + "" + _("(default)"))
)
else:
choices_values.insert(0, self.delete(dump(choice)))
else:
new = []
for idx, val in enumerate(choices_values):
if val in old_default:
choices_values[idx] = (
dump(val) + " " + self.delete("" + _("(default)"))
)
elif val in default:
if val in new_default:
if val in new:
choices_values[idx] = self.underline(
dump(val) + " " + self.bold("" + _("(default)"))
)
else:
choices_values[idx] = (
dump(val)
+ " "
+ self.underline(self.bold("" + _("(default)")))
)
else:
choices_values[idx] = (
dump(val) + " " + self.bold("" + _("(default)"))
)
elif val in new:
choices_values[idx] = self.underline(dump(val))
# if old value and new value is a list, display a list
if not default_is_a_list and len(choices_values) == 1:
choices_values = choices_values[0]
return default_is_already_set, self.section(choices["name"], choices_values)
return default_is_already_set, None
# OTHERs
def to_phrase(self, text: str) -> str:
return text
def display_family_help(self, help_):
return self.to_phrase(help_) + ENTER
def property_to_string(
self,
informations: dict,
calculated_properties: list,
modified_attributes: dict,
) -> str:
"""Transform properties to string"""
properties = []
local_calculated_properties = {}
multi = False
if "properties" in modified_attributes:
previous, new = self.get_modified_properties(
*modified_attributes["properties"][1:]
)
for p, annotation in previous.items():
if p not in new:
properties.append(self.prop(self.delete(p), italic=False))
if annotation is not None:
local_calculated_properties[p] = [self.delete(annotation)]
else:
previous = new = []
for prop in informations.get("properties", []):
if prop["type"] == "type":
properties.append(self.link(prop["name"], ROUGAIL_VARIABLE_TYPE))
else:
if prop["type"] == "multiple":
multi = True
prop_name = prop["name"]
if "annotation" in prop:
italic = True
prop_annotation = prop["annotation"]
if prop_name in new and (
prop_name not in previous
or new[prop_name] != previous[prop_name]
):
prop_annotation = self.underline(prop_annotation)
local_calculated_properties.setdefault(prop["name"], []).append(
prop_annotation
)
else:
italic = False
if prop_name not in previous and prop_name in new:
prop_name = self.underline(prop_name)
properties.append(self.prop(prop_name, italic=italic))
if local_calculated_properties:
for (
calculated_property_name,
calculated_property,
) in local_calculated_properties.items():
if len(calculated_property) > 1:
calculated_property = self.join(calculated_property)
else:
calculated_property = calculated_property[0]
calculated_properties.append(
self.section(
calculated_property_name.capitalize(), calculated_property
)
)
if not properties:
return multi, ""
return multi, " ".join(properties)
def get_modified_properties(
self, previous: List[dict], new: List[dict]
) -> Tuple[dict, dict]:
def modified_properties_parser(dico):
return {d["name"]: d.get("annotation") for d in dico}
return modified_properties_parser(previous), modified_properties_parser(new)
def columns(
self,
col: List[str], # pylint: disable=unused-argument
) -> None:
"""Manage column"""
return
def table(self, datas: list, with_header: bool = True) -> str:
"""Transform list to a table in string format"""
if with_header:
headers = self.table_header([_("Variable"), _("Description")])
else:
headers = ()
msg = (
tabulate(
datas,
headers=headers,
tablefmt=self._table_name,
)
+ "\n\n"
)
datas.clear()
return msg
def message_to_string(self, msg, ret, identifiers=[]):
if isinstance(msg, dict):
if "submessage" in msg:
ret += msg["submessage"]
msg = msg["values"]
elif "message" in msg:
path = calc_path(msg["path"], self, identifiers)
msg = msg["message"].format(path)
return ret, msg
def section(
self,
name: str,
msg: str,
submessage: str = "",
) -> str:
"""Return something like Name: msg"""
submessage, msg = self.message_to_string(msg, submessage)
if isinstance(msg, list):
if len(msg) == 1:
submessage, elt = self.message_to_string(msg[0], submessage)
if isinstance(elt, list):
submessage += self.list(elt)
else:
submessage += elt
else:
lst = []
for p in msg:
submessage, elt = self.message_to_string(p, submessage)
lst.append(elt)
submessage += self.list(lst)
msg = ""
if not isinstance(msg, str):
submessage += dump(msg)
else:
submessage += msg
return _("{0}: {1}").format(self.bold(name), submessage)
def calc_path(path, formater=None, identifiers: List[str] = None) -> str:
def _path_with_identifier(path, identifier):
identifier = normalize_family(str(identifier))
if formater:
identifier = formater.italic(identifier)
return path.replace("{{ identifier }}", identifier, 1)
if isinstance(path, dict):
path_ = path["path"]
for identifier in path["identifiers"]:
path_ = _path_with_identifier(path_, identifier)
elif identifiers:
path_ = path
for identifier in identifiers:
path_ = _path_with_identifier(path_, identifier)
else:
path_ = path
return path_

View file

@ -1,41 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,21 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
...

View file

@ -1,30 +0,0 @@
== New variables
[cols="1a,1a"]
|====
| Variable | Description
|
**family2.var2** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `standard` `mandatory` |
A variable2. +
**Default**: the value of the variable "family.var2".
|
**family2.var3** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `standard` `mandatory` |
A third variable. +
**Default**: string4 +
**Example**: string5
|
**family2.subfamily.variable** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `standard` `mandatory` `unique` `multiple` |
Fourth variable. +
**Default**:
* the value of the variable "var1"
* the value of the variable "family.var2"
* the value of the variable "family2.var3"
|====

View file

@ -1,24 +0,0 @@
New variables
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
family2.var2 │ A variable2. │
 string   standard   mandatory  │ Default: the value of the variable │
│ │ "family.var2". │
├───────────────────────────────────────┼──────────────────────────────────────┤
family2.var3 │ A third variable. │
 string   standard   mandatory  │ Default: string4 │
│ │ Example: string5 │
├───────────────────────────────────────┼──────────────────────────────────────┤
family2.subfamily.variable │ Fourth variable. │
 string   standard   mandatory    │ Default: │
unique   multiple  │ - the value of the variable "var1"
│ │ - the value of the variable │
│ │ "family.var2"
│ │ - the value of the variable │
│ │ "family2.var3"
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,8 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
variable_to_family: # a variable that became a family
...

View file

@ -1,11 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
variable_to_family: # a variable that became a family
var:
description: a second variable
...

View file

@ -1,17 +0,0 @@
== New variable
[cols="1a,1a"]
|====
| Variable | Description
|
**variable_to_family** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
A variable that became a family.
|====
== Deleted variable
* variable_to_family.var

View file

@ -1,18 +0,0 @@
New variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
variable_to_family │ A variable that became a family. │
 string   basic   mandatory  │ │
└───────────────────────────────────────┴──────────────────────────────────────┘
Deleted variable
- variable_to_family.var

View file

@ -1,41 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,39 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,12 +0,0 @@
== New variable
[cols="1a,1a"]
|====
| Variable | Description
|
**family.var3** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
A third variable.
|====

View file

@ -1,11 +0,0 @@
New variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
family.var3 │ A third variable. │
 string   basic   mandatory  │ │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,11 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
variable_to_family: # a variable that became a family
var:
description: a second variable
...

View file

@ -1,8 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
variable_to_family: # a variable that became a family
...

View file

@ -1,17 +0,0 @@
== New variable
[cols="1a,1a"]
|====
| Variable | Description
|
**variable_to_family.var** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
A second variable.
|====
== Deleted variable
* variable_to_family

View file

@ -1,18 +0,0 @@
New variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
variable_to_family.var │ A second variable. │
 string   basic   mandatory  │ │
└───────────────────────────────────────┴──────────────────────────────────────┘
Deleted variable
- variable_to_family

View file

@ -1,43 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
var4: # a fourth variable
...

View file

@ -1,39 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,17 +0,0 @@
== New variables
[cols="1a,1a"]
|====
| Variable | Description
|
**family.var3** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
A third variable.
|
**family2.var4** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
A fourth variable.
|====

View file

@ -1,14 +0,0 @@
New variables
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
family.var3 │ A third variable. │
 string   basic   mandatory  │ │
├───────────────────────────────────────┼──────────────────────────────────────┤
family2.var4 │ A fourth variable. │
 string   basic   mandatory  │ │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,21 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
...

View file

@ -1,41 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,7 +0,0 @@
== Deleted variables
* family2.var2
* family2.var3
* family2.subfamily.variable

View file

@ -1,9 +0,0 @@
Deleted variables
- family2.var2
- family2.var3
- family2.subfamily.variable

View file

@ -1,39 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,41 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,5 +0,0 @@
== Deleted variable
* family.var3

View file

@ -1,7 +0,0 @@
Deleted variable
- family.var3

View file

@ -1,39 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
...

View file

@ -1,43 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
family: # a family
var2:
description: a second variable
test:
- string6
subfamily: # a sub family
variable: # third variable
- variable: ___.var1
- variable: __.var2
var3: # a third variable
family2: # a family
var2:
description: a variable2
default:
variable: __.family.var2
var3:
description: a third variable
test:
- string5
default: string4
subfamily: # a sub family
variable: # fourth variable
- variable: ___.var1
- variable: ___.family.var2
- variable: __.var3
var4: # a fourth variable
...

View file

@ -1,6 +0,0 @@
== Deleted variables
* family.var3
* family2.var4

View file

@ -1,8 +0,0 @@
Deleted variables
- family.var3
- family2.var4

View file

@ -1,6 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # new description
...

View file

@ -1,6 +0,0 @@
%YAML 1.2
---
version: 1.1
var1: # first variable
...

View file

@ -1,13 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
+++First variable.+++ +
#New description.#
|====

View file

@ -1,11 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 string   basic   mandatory  │ New description. │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,8 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
help: modified help
...

View file

@ -1,8 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
help: first help
...

View file

@ -1,14 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
First variable. +
+++First help.+++ +
#Modified help.#
|====

View file

@ -1,12 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 string   basic   mandatory  │ First help. │
│ │ Modified help. │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,13 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid2" %}
not valid2
{% endif %}
description: '"not valid2" is not allowed'
...

View file

@ -1,13 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid1" %}
not valid1
{% endif %}
description: '"not valid1" is not allowed'
...

View file

@ -1,15 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
First variable. +
**Validator**:
+++"not valid1" is not allowed.+++ +
#"not valid2" is not allowed.#
|====

View file

@ -1,13 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 string   basic   mandatory  │ Validator: "not valid1" is not  │
│ │ allowed. │
│ │ "not valid2" is not allowed. │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,18 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid1" %}
not valid1
{% endif %}
description: '"not valid1" is not allowed'
- jinja: |
{% if _.var1 == "not valid3" %}
not valid3
{% endif %}
description: '"not valid3" is not allowed'
...

View file

@ -1,18 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid1" %}
not valid1
{% endif %}
description: '"not valid1" is not allowed'
- jinja: |
{% if _.var1 == "not valid2" %}
not valid2
{% endif %}
description: '"not valid2" is not allowed'
...

View file

@ -1,17 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
First variable. +
**Validators**:
* +++"not valid2" is not allowed.+++
* '"not valid1" is not allowed.'
* '#"not valid3" is not allowed.#'
|====

View file

@ -1,14 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 string   basic   mandatory  │ Validators: │
│ │ - "not valid2" is not allowed. │
│ │ - "not valid1" is not allowed. │
│ │ - "not valid3" is not allowed. │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,23 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid4" %}
not valid4
{% endif %}
description: '"not valid4" is not allowed'
- jinja: |
{% if _.var1 == "not valid2" %}
not valid2
{% endif %}
description: '"not valid2" is not allowed'
- jinja: |
{% if _.var1 == "not valid5" %}
not valid5
{% endif %}
description: '"not valid5" is not allowed'
...

View file

@ -1,23 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
validators:
- jinja: |
{% if _.var1 == "not valid1" %}
not valid1
{% endif %}
description: '"not valid1" is not allowed'
- jinja: |
{% if _.var1 == "not valid2" %}
not valid2
{% endif %}
description: '"not valid2" is not allowed'
- jinja: |
{% if _.var1 == "not valid3" %}
not valid3
{% endif %}
description: '"not valid3" is not allowed'
...

View file

@ -1,19 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[string]` `basic` `mandatory` |
First variable. +
**Validators**:
* +++"not valid1" is not allowed.+++
* +++"not valid3" is not allowed.+++
* '#"not valid4" is not allowed.#'
* '"not valid2" is not allowed.'
* '#"not valid5" is not allowed.#'
|====

View file

@ -1,16 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 string   basic   mandatory  │ Validators: │
│ │ - "not valid1" is not allowed. │
│ │ - "not valid3" is not allowed. │
│ │ - "not valid4" is not allowed. │
│ │ - "not valid2" is not allowed. │
│ │ - "not valid5" is not allowed. │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,11 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- val1
- val2
default: val2
...

View file

@ -1,11 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- val1
- val2
default: val1
...

View file

@ -1,16 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `standard` `mandatory` |
First variable. +
**Choices**:
* val1 +++← (default)+++
* 'val2 #**← (default)**#'
|====

View file

@ -1,13 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 choice   standard   mandatory  │ Choices: │
│ │ - val1 ← (default) │
│ │ - val2 ← (default) │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,16 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: val1 # the first source variable
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- variable: _.source_variable_1
- variable: _.source_variable_2
default:
variable: _.source_variable_1
...

View file

@ -1,15 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: val1 # the first source variable
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- val1
- val2
default: val1
...

View file

@ -1,18 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**my_variable** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `standard` `mandatory` |
A variable. +
**Choices**:
* +++val1 ← (default)+++
* +++val2+++
* '#the value of the variable "source_variable_1" **← (default)**#'
* '#the value of the variable "source_variable_2"#'
|====

View file

@ -1,17 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
my_variable │ A variable. │
 choice   standard   mandatory  │ Choices: │
│ │ - val1 ← (default) │
│ │ - val2 │
│ │ - the value of the variable  │
│ │ "source_variable_1" ← (default) │
│ │ - the value of the variable  │
│ │ "source_variable_2" │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,15 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: val1 # the first source variable
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- val1
- val2
default: val1
...

View file

@ -1,16 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: val1 # the first source variable
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- variable: _.source_variable_1
- variable: _.source_variable_2
default:
variable: _.source_variable_1
...

View file

@ -1,18 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**my_variable** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `standard` `mandatory` |
A variable. +
**Choices**:
* +++the value of the variable "source_variable_1" ← (default)+++
* +++the value of the variable "source_variable_2"+++
* '#val1 **← (default)**#'
* '#val2#'
|====

View file

@ -1,17 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
my_variable │ A variable. │
 choice   standard   mandatory  │ Choices: │
│ │ - the value of the variable  │
│ │ "source_variable_1"(default) │
│ │ - the value of the variable  │
│ │ "source_variable_2" │
│ │ - val1 ← (default) │
│ │ - val2 │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,15 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: # the first source variable
- val1
- val2
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
variable: _.source_variable_1
...

View file

@ -1,16 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: # the first source variable
- val1
- val2
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- val1
- val2
...

View file

@ -1,17 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**my_variable** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `basic` `mandatory` |
A variable. +
**Choices**:
* +++val1+++
* +++val2+++
* '#the value of the variable "source_variable_1".#'
|====

View file

@ -1,15 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
my_variable │ A variable. │
 choice   basic   mandatory  │ Choices: │
│ │ - val1 │
│ │ - val2 │
│ │ - the value of the variable  │
│ │ "source_variable_1". │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,16 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: # the first source variable
- val1
- val2
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
- val1
- val2
...

View file

@ -1,15 +0,0 @@
%YAML 1.2
---
version: 1.1
source_variable_1: # the first source variable
- val1
- val2
source_variable_2: val2 # the second source variable
my_variable:
description: a variable
choices:
variable: _.source_variable_1
...

View file

@ -1,17 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**my_variable** +
`https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `basic` `mandatory` |
A variable. +
**Choices**:
* +++the value of the variable "source_variable_1".+++
* '#val1#'
* '#val2#'
|====

View file

@ -1,15 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
my_variable │ A variable. │
 choice   basic   mandatory  │ Choices: │
│ │ - the value of the variable  │
│ │ "source_variable_1". │
│ │ - val1 │
│ │ - val2 │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,11 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- val1
- val2
default: val2
...

View file

@ -1,10 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- val1
- val2
...

View file

@ -1,16 +0,0 @@
== Modified variable
[cols="1a,1a"]
|====
| Variable | Description
|
**var1** +
`+++basic+++` `https://rougail.readthedocs.io/en/latest/variable.html#variables-types[choice]` `#standard#` `mandatory` |
First variable. +
**Choices**:
* val1
* 'val2 #**← (default)**#'
|====

View file

@ -1,13 +0,0 @@
Modified variable
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Variable  ┃ Description  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
var1 │ First variable. │
 basic   choice   standard    │ Choices: │
mandatory  │ - val1 │
│ │ - val2 ← (default) │
└───────────────────────────────────────┴──────────────────────────────────────┘

View file

@ -1,12 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- null
- val1
- val2
default: val2
...

View file

@ -1,12 +0,0 @@
%YAML 1.2
---
version: 1.1
var1:
description: first variable
choices:
- null
- val1
- val2
default: null
...

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