Compare commits

...

6 commits

80 changed files with 794 additions and 410 deletions

View file

@ -1,222 +1,59 @@
## 0.2.0a32 (2026-06-11) ## 1.0.0 (2026-06-21)
### Fix
- attributeerror
## 0.2.0a31 (2026-05-04)
### Feat ### Feat
- multi layers - multi layers
## 0.2.0a30 (2026-01-21)
### Fix
- leadership with smaller default value
## 0.2.0a29 (2026-01-21)
### Fix
- update tests
- secret_manager has no real default value
## 0.2.0a28 (2026-01-16)
### Fix
- display with subconfig
## 0.2.0a27 (2026-01-14)
### Fix
- modified value without layer
## 0.2.0a26 (2026-01-05)
### Fix
- enter in layers list
## 0.2.0a25 (2026-01-01)
### Fix
- better layers support
## 0.2.0a24 (2026-01-01)
### Fix
- error only if default
## 0.2.0a23 (2026-01-01)
### Feat
- add layers + gitlab - add layers + gitlab
## 0.2.0a22 (2025-12-30)
### Fix
- update tests
## 0.2.0a21 (2025-12-22)
### Fix
- console => display
## 0.2.0a20 (2025-12-22)
### Feat
- github output - github output
- document output - document output
## 0.2.0a19 (2025-11-21)
### Feat
- undocumented_modes is now a variable - undocumented_modes is now a variable
### Fix
- black
- do not display empty console
## 0.2.0a18 (2025-11-07)
### Feat
- display layers if actived - display layers if actived
## 0.2.0a17 (2025-11-06)
### Feat
- support values of differents layers - support values of differents layers
## 0.2.0a16 (2025-11-03)
### Feat
- support user data layers - support user data layers
## 0.2.0a15 (2025-10-16)
### Fix
- error format
## 0.2.0a14 (2025-10-10)
### Feat
- display error with tree - display error with tree
### Fix
- update tests
- tests for formatter
## 0.2.0a13 (2025-09-29)
### Feat
- remove console.read_write option - remove console.read_write option
- default value for a calculated variable with an unknown optional variable - default value for a calculated variable with an unknown optional variable
- update tests for integer type - update tests for integer type
## 0.2.0a12 (2025-09-22)
### Fix
- dictionary => structure
## 0.2.0a11 (2025-05-12)
### Feat
- display loaded_from informations - display loaded_from informations
### Fix
- black
## 0.2.0a10 (2025-05-09)
### Fix
- the key is the description by default instead of name
## 0.2.0a9 (2025-04-30)
### Fix
- remove negative_description support
- add space before ":" in french
## 0.2.0a8 (2025-04-10)
### Fix
- version
## 0.2.0a7 (2025-04-09)
### Fix
- version
## 0.2.0a6 (2025-04-09)
### Fix
- better output for mandatories variables
## 0.2.0a5 (2025-04-09)
### Fix
- better caption + do not display None value
## 0.2.0a4 (2025-04-01)
### Fix
- update translation
## 0.2.0a3 (2025-02-17)
### Fix
- do not display default value with default_value_makes_sense set to False
- translate ERRORS
## 0.2.0a2 (2025-02-10)
### Feat
- output return status too - output return status too
- add tests - add tests
- exporter to console
## 0.2.0a1 (2024-11-28)
### Fix ### Fix
- rougail dependencies
- black
- update tests
- attributeerror
- leadership with smaller default value
- update tests
- secret_manager has no real default value
- display with subconfig
- modified value without layer
- enter in layers list
- better layers support
- error only if default
- update tests
- console => display
- black
- do not display empty console
- error format
- update tests
- tests for formatter
- dictionary => structure
- black
- the key is the description by default instead of name
- remove negative_description support
- add space before ":" in french
- version
- version
- better output for mandatories variables
- better caption + do not display None value
- update translation
- do not display default value with default_value_makes_sense set to False
- translate ERRORS
- separation between run and print function - separation between run and print function
## 0.2.0a0 (2024-11-27)
### Feat
- exporter to console
## 0.1.0 (2024-11-06) ## 0.1.0 (2024-11-06)
## 0.1.0rc1 (2024-11-06) ## 0.1.0rc1 (2024-11-06)

View file

@ -11,13 +11,12 @@ include_toc: true
> Retrouver toutes les variables et leurs valeurs de votre configuration (structures et données utilisateurs). Des informations complémentaires sont disponibles, comme la valeur par défaut, le lieu de chargement de la valeur, ...\ > Retrouver toutes les variables et leurs valeurs de votre configuration (structures et données utilisateurs). Des informations complémentaires sont disponibles, comme la valeur par défaut, le lieu de chargement de la valeur, ...\
> **Chemin** : display\ > **Chemin** : display\
> *`désactivé`*\ > *`désactivé`*\
> **Désactivé** : si display n'est pas défini dans "[Sélection pour sortie](#step.output)" > **Désactivé** : si display n'est pas défini dans "[sélection pour sortie](#step.output)".
| Variable | Description | Valeur par défaut | Type | Validateur | | Variable | Description | Valeur par défaut | Type | Validateur |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------| |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|
| **<a id="display.output_format" name="display.output_format">display.output_format</a>**<br/>**Ligne de commande** : <br/>--display.output_format<br/>**Variable d'environnement** : DISPLAY.OUTPUT_FORMAT | Le format de sortie pour afficher des variables. | console | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | **Choix** : <br/>&nbsp;console<br/>&nbsp;github<br/>&nbsp;gitlab | | **<a id="display.output_format" name="display.output_format">display.output_format</a>**<br/>**Ligne de commande** : <br/>--display.output_format<br/>**Variable d'environnement** : ROUGAILCLI_DISPLAY.OUTPUT_FORMAT | Le format de sortie pour afficher des variables. | console | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | **Choix** : <br/>&nbsp;console<br/>&nbsp;github<br/>&nbsp;gitlab |
| **<a id="display.show_secrets" name="display.show_secrets">display.show_secrets</a>**<br/>**Ligne de commande** : <br/>&nbsp;--display.show_secrets<br/>&nbsp;--display.no-show_secrets<br/>**Variable d'environnement** : DISPLAY.SHOW_SECRETS | Voir les secrets plutôt que de les obscurcir. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | | | **<a id="display.show_secrets" name="display.show_secrets">display.show_secrets</a>**<br/>**Ligne de commande** : <br/>&nbsp;--display.show_secrets<br/>&nbsp;--display.no-show_secrets<br/>**Variable d'environnement** : ROUGAILCLI_DISPLAY.SHOW_SECRETS | Voir les secrets plutôt que de les obscurcir. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | |
| **<a id="display.mandatory" name="display.mandatory">display.mandatory</a>**<br/>**Ligne de commande** : <br/>&nbsp;--display.mandatory<br/>&nbsp;--display.no-mandatory<br/>**Variable d'environnement** : DISPLAY.MANDATORY | Tester les variables obligatoires avant de les afficher. | ne pas tester si "[La configuration dans l&#x27;état de sortie est dans mode lecture-écriture](#cli.read_write)" est activé | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | |
### Configuration spécifique pour la sortie console ### Configuration spécifique pour la sortie console
@ -25,10 +24,8 @@ include_toc: true
> >
> **Chemin** : display.console\ > **Chemin** : display.console\
> *`désactivé`*\ > *`désactivé`*\
> **Désactivé** : lorsque la variable "[Le format de sortie pour afficher des variables](#display.output_format)" n'a pas la valeur "console" > **Désactivé** : lorsque la variable "[Le format de sortie pour afficher des variables](#display.output_format)" n'a pas la valeur "console".
| Variable | Description | Type | Validateur |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------|----------------------------|
| **<a id="display.console.max_width" name="display.console.max_width">display.console.max_width</a>**<br/>**Ligne de commande** : <br/>--display.console.max_width<br/>**Variable d'environnement** : DISPLAY.CONSOLE.MAX_WIDTH | Nombre maximal de caractères par ligne.<br/>Null signifie illimité. | [`integer`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) | La valeur minimale est 50. |
| Variable | Description | Type | Validateur |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------|----------------------------|
| **<a id="display.console.max_width" name="display.console.max_width">display.console.max_width</a>**<br/>**Ligne de commande** : <br/>--display.console.max_width<br/>**Variable d'environnement** : ROUGAILCLI_DISPLAY.CONSOLE.MAX_WIDTH | Nombre maximal de caractères par ligne.<br/>Null signifie illimité. | [`integer`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) | La valeur minimale est 50. |

View file

@ -11,13 +11,12 @@ include_toc: true
> Find all the variables and their values in your configuration (structural and user data). Additional informations are available, such as the default value, the location where the value is loaded, etc.\ > Find all the variables and their values in your configuration (structural and user data). Additional informations are available, such as the default value, the location where the value is loaded, etc.\
> **Path**: display\ > **Path**: display\
> *`disabled`*\ > *`disabled`*\
> **Disabled**: if display is not set in "[Select for output](#step.output)" > **Disabled**: if display is not set in "[select for output](#step.output)".
| Variable | Description | Default value | Type | Validator | | Variable | Description | Default value | Type | Validator |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------|------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|-----------------|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
| **<a id="display.output_format" name="display.output_format">display.output_format</a>**<br/>**Command line**: <br/>--display.output_format<br/>**Environment variable**: DISPLAY.OUTPUT_FORMAT | The output format for displaying variables. | console | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | **Choices**: <br/>&nbsp;console<br/>&nbsp;github<br/>&nbsp;gitlab | | **<a id="display.output_format" name="display.output_format">display.output_format</a>**<br/>**Command line**: <br/>--display.output_format<br/>**Environment variable**: ROUGAILCLI_DISPLAY.OUTPUT_FORMAT | The output format for displaying variables. | console | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | **Choices**: <br/>&nbsp;console<br/>&nbsp;github<br/>&nbsp;gitlab |
| **<a id="display.show_secrets" name="display.show_secrets">display.show_secrets</a>**<br/>**Command line**: <br/>&nbsp;--display.show_secrets<br/>&nbsp;--display.no-show_secrets<br/>**Environment variable**: DISPLAY.SHOW_SECRETS | Show secrets instead of obscuring them. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | | | **<a id="display.show_secrets" name="display.show_secrets">display.show_secrets</a>**<br/>**Command line**: <br/>&nbsp;--display.show_secrets<br/>&nbsp;--display.no-show_secrets<br/>**Environment variable**: ROUGAILCLI_DISPLAY.SHOW_SECRETS | Show secrets instead of obscuring them. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | |
| **<a id="display.mandatory" name="display.mandatory">display.mandatory</a>**<br/>**Command line**: <br/>&nbsp;--display.mandatory<br/>&nbsp;--display.no-mandatory<br/>**Environment variable**: DISPLAY.MANDATORY | Test mandatories variables before display in display. | do not test if "[Configuration in output step is in read_write mode](#cli.read_write)" is true | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | |
### Specific configuration for console output ### Specific configuration for console output
@ -25,10 +24,8 @@ include_toc: true
> >
> **Path**: display.console\ > **Path**: display.console\
> *`disabled`*\ > *`disabled`*\
> **Disabled**: when the variable "[The output format for displaying variables](#display.output_format)" hasn't the value "console" > **Disabled**: when the variable "[The output format for displaying variables](#display.output_format)" hasn't the value "console".
| Variable | Description | Type | Validator |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------|--------------------------|
| **<a id="display.console.max_width" name="display.console.max_width">display.console.max_width</a>**<br/>**Command line**: <br/>--display.console.max_width<br/>**Environment variable**: DISPLAY.CONSOLE.MAX_WIDTH | Maximum number of characters per line.<br/>Null means unlimited. | [`integer`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) | The minimum value is 50. |
| Variable | Description | Type | Validator |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------|--------------------------|
| **<a id="display.console.max_width" name="display.console.max_width">display.console.max_width</a>**<br/>**Command line**: <br/>--display.console.max_width<br/>**Environment variable**: ROUGAILCLI_DISPLAY.CONSOLE.MAX_WIDTH | Maximum number of characters per line.<br/>Null means unlimited. | [`integer`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) | The minimum value is 50. |

View file

@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2026-01-01 09:20+0100\n" "POT-Creation-Date: 2026-06-15 08:57+0200\n"
"PO-Revision-Date: 2026-01-01 09:20+0100\n" "PO-Revision-Date: 2026-06-15 08:57+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n" "Language: fr\n"
@ -14,7 +14,7 @@ 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.8\n" "X-Generator: Poedit 3.9\n"
#: src/rougail/output_display/config.py:44 #: src/rougail/output_display/config.py:44
msgid "" msgid ""
@ -55,34 +55,18 @@ msgid "Show secrets instead of obscuring them"
msgstr "Voir les secrets plutôt que de les obscurcir" msgstr "Voir les secrets plutôt que de les obscurcir"
#: src/rougail/output_display/config.py:106 #: src/rougail/output_display/config.py:106
msgid "test mandatories variables before display in display"
msgstr "tester les variables obligatoires avant de les afficher"
#: src/rougail/output_display/config.py:115
msgid "do not test if \"cli.read_write\" is true"
msgstr "ne pas tester si \"cli.read_write\" est activé"
#: src/rougail/output_display/config.py:118
msgid "Specific configuration for console output" msgid "Specific configuration for console output"
msgstr "Configuration spécifique pour la sortie console" msgstr "Configuration spécifique pour la sortie console"
#: src/rougail/output_display/config.py:124 #: src/rougail/output_display/config.py:112
msgid "Maximum number of characters per line" msgid "Maximum number of characters per line"
msgstr "Nombre maximal de caractères par ligne" msgstr "Nombre maximal de caractères par ligne"
#: src/rougail/output_display/config.py:125 #: src/rougail/output_display/config.py:113
msgid "null means unlimited" msgid "null means unlimited"
msgstr "null signifie illimité" msgstr "null signifie illimité"
#: src/rougail/output_display/display.py:153 #: src/rougail/output_display/display.py:137
msgid "mandatory variable but is inaccessible and has no value"
msgstr "variable obligatoire mais est inaccessible et n'a pas de valeur"
#: src/rougail/output_display/display.py:159
msgid "mandatory variable but has no value"
msgstr "variable obligatoire mais n'a pas de valeur"
#: src/rougail/output_display/display.py:191
msgid "Variables:" msgid "Variables:"
msgstr "Variables :" msgstr "Variables :"
@ -115,24 +99,24 @@ msgstr "Valeur par défaut d'origine"
msgid "Caption" msgid "Caption"
msgstr "Légende" msgstr "Légende"
#: src/rougail/output_display/output/console.py:89 #: src/rougail/output_display/output/console.py:78
msgid "Layers" msgid "Layers"
msgstr "Couches" msgstr "Couches"
#: src/rougail/output_display/output/console.py:93 #: src/rougail/output_display/output/console.py:82
msgid "Caution" msgid "Caution"
msgstr "Attention" msgstr "Attention"
#: src/rougail/output_display/output/console.py:131 #: src/rougail/output_display/output/console.py:120
msgid "Warning" msgid "Warning"
msgstr "Avertissement" msgstr "Avertissement"
#: src/rougail/output_display/output/console.py:151 #: src/rougail/output_display/output/console.py:140
#: src/rougail/output_display/output/github.py:126 #: src/rougail/output_display/output/github.py:126
msgid "{0}:" msgid "{0}:"
msgstr "{0} :" msgstr "{0} :"
#: src/rougail/output_display/output/console.py:157 #: src/rougail/output_display/output/console.py:146
#: src/rougail/output_display/output/github.py:132 #: src/rougail/output_display/output/github.py:132
msgid "{0}: {1}" msgid "{0}: {1}"
msgstr "{0} : {1}" msgstr "{0} : {1}"
@ -145,6 +129,18 @@ msgstr "Légende :"
msgid "Layers:" msgid "Layers:"
msgstr "Couches :" msgstr "Couches :"
#~ msgid "test mandatories variables before display in display"
#~ msgstr "tester les variables obligatoires avant de les afficher"
#~ msgid "do not test if \"cli.read_write\" is true"
#~ msgstr "ne pas tester si \"cli.read_write\" est activé"
#~ msgid "mandatory variable but is inaccessible and has no value"
#~ msgstr "variable obligatoire mais est inaccessible et n'a pas de valeur"
#~ msgid "mandatory variable but has no value"
#~ msgstr "variable obligatoire mais n'a pas de valeur"
#~ msgid "Error in config: {0}" #~ msgid "Error in config: {0}"
#~ msgstr "Erreur dans la config : {0}" #~ msgstr "Erreur dans la config : {0}"

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2026-01-01 09:20+0100\n" "POT-Creation-Date: 2026-06-15 08:57+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -44,34 +44,18 @@ msgid "Show secrets instead of obscuring them"
msgstr "" msgstr ""
#: src/rougail/output_display/config.py:106 #: src/rougail/output_display/config.py:106
msgid "test mandatories variables before display in display"
msgstr ""
#: src/rougail/output_display/config.py:115
msgid "do not test if \"cli.read_write\" is true"
msgstr ""
#: src/rougail/output_display/config.py:118
msgid "Specific configuration for console output" msgid "Specific configuration for console output"
msgstr "" msgstr ""
#: src/rougail/output_display/config.py:124 #: src/rougail/output_display/config.py:112
msgid "Maximum number of characters per line" msgid "Maximum number of characters per line"
msgstr "" msgstr ""
#: src/rougail/output_display/config.py:125 #: src/rougail/output_display/config.py:113
msgid "null means unlimited" msgid "null means unlimited"
msgstr "" msgstr ""
#: src/rougail/output_display/display.py:153 #: src/rougail/output_display/display.py:137
msgid "mandatory variable but is inaccessible and has no value"
msgstr ""
#: src/rougail/output_display/display.py:159
msgid "mandatory variable but has no value"
msgstr ""
#: src/rougail/output_display/display.py:191
msgid "Variables:" msgid "Variables:"
msgstr "" msgstr ""
@ -104,24 +88,24 @@ msgstr ""
msgid "Caption" msgid "Caption"
msgstr "" msgstr ""
#: src/rougail/output_display/output/console.py:89 #: src/rougail/output_display/output/console.py:78
msgid "Layers" msgid "Layers"
msgstr "" msgstr ""
#: src/rougail/output_display/output/console.py:93 #: src/rougail/output_display/output/console.py:82
msgid "Caution" msgid "Caution"
msgstr "" msgstr ""
#: src/rougail/output_display/output/console.py:131 #: src/rougail/output_display/output/console.py:120
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: src/rougail/output_display/output/console.py:151 #: src/rougail/output_display/output/console.py:140
#: src/rougail/output_display/output/github.py:126 #: src/rougail/output_display/output/github.py:126
msgid "{0}:" msgid "{0}:"
msgstr "" msgstr ""
#: src/rougail/output_display/output/console.py:157 #: src/rougail/output_display/output/console.py:146
#: src/rougail/output_display/output/github.py:132 #: src/rougail/output_display/output/github.py:132
msgid "{0}: {1}" msgid "{0}: {1}"
msgstr "" msgstr ""

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "rougail.output_display" name = "rougail.output_display"
version = "0.2.0a32" version = "1.0.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 display" description = "Rougail output display"
@ -24,7 +24,7 @@ classifiers = [
] ]
dependencies = [ dependencies = [
"rougail >= 1.1,<2", "rougail >= 1.2.0,<2",
"rich ~= 13.9.3", "rich ~= 13.9.3",
] ]

View file

@ -1,7 +1,7 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2025-2026 Copyright (C) 2025-2026
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
Free Software Foundation, either version 3 of the License, or (at your Free Software Foundation, either version 3 of the License, or (at your
@ -15,6 +15,7 @@ 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/>.
""" """
from .display import RougailOutputDisplay from .display import RougailOutputDisplay
from .__version__ import __version__ from .__version__ import __version__

View file

@ -41,9 +41,9 @@ def get_outputs() -> None:
level = obj_class.level level = obj_class.level
if level in outputs: if level in outputs:
raise ImportError( raise ImportError(
_('duplicated level in rougail-output-display for output "{0}": {1} and {2}').format( _(
level, obj_class.name, outputs[level].name 'duplicated level in rougail-output-display for output "{0}": {1} and {2}'
) ).format(level, obj_class.name, outputs[level].name)
) )
if obj_class.name in names: if obj_class.name in names:
raise ImportError( raise ImportError(

View file

@ -20,7 +20,12 @@ from typing import Optional, Any
from ruamel.yaml import YAML from ruamel.yaml import YAML
from io import BytesIO from io import BytesIO
from tiramisu.error import PropertiesOptionError, LeadershipError, ConfigError, AttributeOptionError from tiramisu.error import (
PropertiesOptionError,
LeadershipError,
ConfigError,
AttributeOptionError,
)
from tiramisu import owners, groups from tiramisu import owners, groups
from .config import OutPuts from .config import OutPuts
@ -43,6 +48,7 @@ class RougailOutputDisplay:
) -> None: ) -> None:
if rougailconfig is None: if rougailconfig is None:
from rougail import RougailConfig from rougail import RougailConfig
rougailconfig = RougailConfig rougailconfig = RougailConfig
self.rougailconfig = rougailconfig self.rougailconfig = rougailconfig
self.config = config self.config = config
@ -61,7 +67,7 @@ class RougailOutputDisplay:
if user_data_warnings is None: if user_data_warnings is None:
user_data_warnings = [] user_data_warnings = []
self.user_data_warnings = user_data_warnings self.user_data_warnings = user_data_warnings
# self.out = [] # self.out = []
self.nodes = None self.nodes = None
self.yaml = YAML() self.yaml = YAML()
self.yaml.indent(mapping=2, sequence=4, offset=2) self.yaml.indent(mapping=2, sequence=4, offset=2)
@ -75,27 +81,36 @@ class RougailOutputDisplay:
errors_warnings_dict = {} errors_warnings_dict = {}
if not errors and self.nodes is None: if not errors and self.nodes is None:
show_secrets = self.rougailconfig["display.show_secrets"] show_secrets = self.rougailconfig["display.show_secrets"]
self.nodes = Node(self.yaml, show_secrets, self.config, self.root_config, self.config_owner_is_path, errors) self.nodes = Node(
self.yaml,
show_secrets,
self.config,
self.root_config,
self.config_owner_is_path,
errors,
)
if warnings: if warnings:
level = "warnings" level = "warnings"
#output.display_warnings(errors_warnings_dict, warnings) # output.display_warnings(errors_warnings_dict, warnings)
for warning in warnings: for warning in warnings:
output.error_warn_to_dict(warning, errors_warnings_dict, 'warning') output.error_warn_to_dict(warning, errors_warnings_dict, "warning")
if errors: if errors:
level = "errors" level = "errors"
for error in errors: for error in errors:
output.error_warn_to_dict(error, errors_warnings_dict, 'error') output.error_warn_to_dict(error, errors_warnings_dict, "error")
if level: if level:
if level == "errors": if level == "errors":
tree = output.error_header() tree = output.error_header()
else: else:
tree = output.warning_header() tree = output.warning_header()
ret = output.parse_error_warning(tree, errors_warnings_dict, output.display_error, level) ret = output.parse_error_warning(
tree, errors_warnings_dict, output.display_error, level
)
if errors: if errors:
return False, ret return False, ret
ret += "\n" ret += "\n"
else: else:
ret = '' ret = ""
code, run = output.run(self.nodes, self.layer_datas) code, run = output.run(self.nodes, self.layer_datas)
return code, ret + run return code, ret + run
@ -116,10 +131,10 @@ class Node:
errors, errors,
*, *,
node=None, node=None,
values: Optional[dict]=None, values: Optional[dict] = None,
level: int=0, level: int = 0,
leader_index: Optional[int]=None, leader_index: Optional[int] = None,
) -> None: ) -> None:
self.yaml = yaml self.yaml = yaml
self.show_secrets = show_secrets self.show_secrets = show_secrets
self.config = config self.config = config
@ -141,7 +156,9 @@ class Node:
if node and node.isoptiondescription() and node.isleadership(): if node and node.isoptiondescription() and node.isleadership():
values_iter = iter(values.items()) values_iter = iter(values.items())
leader, leader_values = next(values_iter) leader, leader_values = next(values_iter)
followers_values = {idx: {leader: value} for idx, value in enumerate(leader_values)} followers_values = {
idx: {leader: value} for idx, value in enumerate(leader_values)
}
for follower, follower_value in values_iter: for follower, follower_value in values_iter:
followers_values[follower.index()][follower] = follower_value followers_values[follower.index()][follower] = follower_value
for idx, fvalues in enumerate(followers_values.values()): for idx, fvalues in enumerate(followers_values.values()):
@ -174,25 +191,33 @@ class Node:
option, option,
values, values,
*, *,
leader_index: Optional[int]=None, leader_index: Optional[int] = None,
) -> 'Node': ) -> "Node":
self.children.append({"type": "node", self.children.append(
"node": Node( {
self.yaml, "type": "node",
self.show_secrets, "node": Node(
self.config, self.yaml,
self.root_config, self.show_secrets,
self.config_owner_is_path, self.config,
self.errors, self.root_config,
node=option, self.config_owner_is_path,
values=values, self.errors,
level=self.level + 1, node=option,
leader_index=leader_index, values=values,
), level=self.level + 1,
}) leader_index=leader_index,
),
}
)
def add_leaf( def add_leaf(
self, option, value: Any, *, leader_index: Optional[int] = None, description: Optional[str] = None self,
option,
value: Any,
*,
leader_index: Optional[int] = None,
description: Optional[str] = None,
): ):
properties = option.property.get() properties = option.property.get()
if description is None: if description is None:
@ -200,15 +225,23 @@ class Node:
icon = "leaf" icon = "leaf"
else: else:
icon = "node" icon = "node"
self.children.append({"type": "leaf", self.children.append(
"description": description, {
"values": self.get_values(option, value, leader_index, properties), "type": "leaf",
"icon": icon, "description": description,
"hidden": "hidden" in properties, "values": self.get_values(option, value, leader_index, properties),
}, "icon": icon,
) "hidden": "hidden" in properties,
},
)
def get_values(self, option: "Option", value: Any, leader_index: Optional[int], properties: list[str]) -> None: def get_values(
self,
option: "Option",
value: Any,
leader_index: Optional[int],
properties: list[str],
) -> None:
if option.isoptiondescription(): if option.isoptiondescription():
return [{"is_default": True, "value": value, "loaded_from": None}] return [{"is_default": True, "value": value, "loaded_from": None}]
values = [] values = []
@ -219,7 +252,7 @@ class Node:
loaded_from_key = f"loaded_from_{index}" loaded_from_key = f"loaded_from_{index}"
else: else:
loaded_from_key = "loaded_from" loaded_from_key = "loaded_from"
#default = option.owner.isdefault() or not option.information.get("default_value_makes_sense", True) # default = option.owner.isdefault() or not option.information.get("default_value_makes_sense", True)
force_store_value = "force_store_value" in properties force_store_value = "force_store_value" in properties
is_default = option.owner.isdefault() is_default = option.owner.isdefault()
option_path = option.path() option_path = option.path()
@ -230,7 +263,7 @@ class Node:
else: else:
true_default = is_default and meta_option.owner.get() == default_owner true_default = is_default and meta_option.owner.get() == default_owner
added = not is_default or true_default added = not is_default or true_default
secret_manager = option.information.get('secret_manager', False) secret_manager = option.information.get("secret_manager", False)
while True: while True:
if values and true_default and (value in [None, []] or secret_manager): if values and true_default and (value in [None, []] or secret_manager):
break break
@ -256,7 +289,9 @@ class Node:
if is_default and (not meta_config_path or "." not in meta_config_path): if is_default and (not meta_config_path or "." not in meta_config_path):
# we are in root metaconfig and we have default value # we are in root metaconfig and we have default value
break break
new_meta_config = self.get_metaconfig_with_default_value(meta_config, meta_option) new_meta_config = self.get_metaconfig_with_default_value(
meta_config, meta_option
)
if not new_meta_config: if not new_meta_config:
break break
meta_option = new_meta_config.option(option_path, index) meta_option = new_meta_config.option(option_path, index)
@ -297,11 +332,7 @@ class Node:
value: Any, value: Any,
) -> str: ) -> str:
"""Dump variable, means transform bool, ... to yaml string""" """Dump variable, means transform bool, ... to yaml string"""
if ( if value is not None and not self.show_secrets and option.type() == "password":
value is not None
and not self.show_secrets
and option.type() == "password"
):
return "*" * 10 return "*" * 10
if isinstance(value, str): if isinstance(value, str):
return value return value
@ -322,7 +353,7 @@ class Node:
return self.config return self.config
if not self.config_owner_is_path: if not self.config_owner_is_path:
while True: while True:
if meta_config.type() != 'metaconfig': if meta_config.type() != "metaconfig":
return None return None
meta_config = meta_config.parent() meta_config = meta_config.parent()
if not meta_config.owner.isdefault(): if not meta_config.owner.isdefault():

View file

@ -1,7 +1,7 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2026 Copyright (C) 2022-2026
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
Free Software Foundation, either version 3 of the License, or (at your Free Software Foundation, either version 3 of the License, or (at your
@ -33,7 +33,7 @@ class OutputFamily(CommonOutput):
value_unmodified_color = "gold1" value_unmodified_color = "gold1"
value_modified_color = "green" value_modified_color = "green"
value_default_color = None value_default_color = None
error_color = 'bright_red' error_color = "bright_red"
error_icon = "stop_sign" error_icon = "stop_sign"
warning_color = "bright_yellow" warning_color = "bright_yellow"
warning_icon = "bell" warning_icon = "bell"
@ -56,14 +56,23 @@ class OutputFamily(CommonOutput):
if self.variable_default_enable: if self.variable_default_enable:
caption_line += _("Variable") + "\n" caption_line += _("Variable") + "\n"
if self.variable_hidden_enable: if self.variable_hidden_enable:
caption_line += self.set_color(self.variable_hidden_color, _("Unmodifiable variable")) + "\n" caption_line += (
self.set_color(self.variable_hidden_color, _("Unmodifiable variable"))
+ "\n"
)
header_value = "" header_value = ""
if self.value_unmodified_enable: if self.value_unmodified_enable:
header_value += self.set_color(self.value_unmodified_color, _("Default value")) + "\n" header_value += (
self.set_color(self.value_unmodified_color, _("Default value")) + "\n"
)
if self.value_modified_enable: if self.value_modified_enable:
header_value += self.set_color(self.value_modified_color, _("Modified value")) + "\n" header_value += (
self.set_color(self.value_modified_color, _("Modified value")) + "\n"
)
if self.value_default_enable: if self.value_default_enable:
header_value += f'(:hourglass_flowing_sand: {_("Original default value")})\n' header_value += (
f'(:hourglass_flowing_sand: {_("Original default value")})\n'
)
caption = Table.grid(padding=1, collapse_padding=True) caption = Table.grid(padding=1, collapse_padding=True)
caption.pad_edge = False caption.pad_edge = False
caption.add_row(caption_line[:-1], header_value[:-1]) caption.add_row(caption_line[:-1], header_value[:-1])
@ -82,7 +91,7 @@ class OutputFamily(CommonOutput):
f"[bold][{self.error_color}]:{self.error_icon}: {_('Caution')}[/{self.error_color}][/bold]", f"[bold][{self.error_color}]:{self.error_icon}: {_('Caution')}[/{self.error_color}][/bold]",
guide_style=f"bold {self.error_color}", guide_style=f"bold {self.error_color}",
) )
# self.out.append(tree) # self.out.append(tree)
return tree return tree
def display_error(self, level, tree, msg, default_color): def display_error(self, level, tree, msg, default_color):
@ -92,7 +101,7 @@ class OutputFamily(CommonOutput):
color = self.warning_color color = self.warning_color
if isinstance(msg, tuple): if isinstance(msg, tuple):
if len(msg) == 3: if len(msg) == 3:
if msg[2] == 'error': if msg[2] == "error":
color = self.error_color color = self.error_color
icon = self.error_icon icon = self.error_icon
else: else:
@ -100,19 +109,19 @@ class OutputFamily(CommonOutput):
icon = self.warning_icon icon = self.warning_icon
msg = f"{msg[0]}: [{color}]:{icon}: {msg[1]}[/{color}]" msg = f"{msg[0]}: [{color}]:{icon}: {msg[1]}[/{color}]"
else: else:
if msg[1] == 'error': if msg[1] == "error":
icon = self.error_icon icon = self.error_icon
else: else:
color = self.warning_color color = self.warning_color
icon = self.warning_icon icon = self.warning_icon
msg = f"[{color}]:{icon}: {msg[0]}[/{color}]" msg = f"[{color}]:{icon}: {msg[0]}[/{color}]"
tree.guide_style = f'bold {color}' tree.guide_style = f"bold {color}"
return tree.add(msg, guide_style=f"bold {color}") return tree.add(msg, guide_style=f"bold {color}")
def display(self, tree): def display(self, tree):
console = Console(force_terminal=True, width=self.max_width) console = Console(force_terminal=True, width=self.max_width)
with console.capture() as capture: with console.capture() as capture:
console.print(tree) console.print(tree)
return capture.get() return capture.get()
def warning_header(self): def warning_header(self):
@ -120,21 +129,27 @@ class OutputFamily(CommonOutput):
f"[bold][{self.warning_color}]:{self.warning_icon}: {_('Warning')}[/{self.warning_color}][/bold]", f"[bold][{self.warning_color}]:{self.warning_icon}: {_('Warning')}[/{self.warning_color}][/bold]",
guide_style=f"bold {self.warning_color}", guide_style=f"bold {self.warning_color}",
) )
# self.out.append(tree) # self.out.append(tree)
return tree return tree
#
# def display_warning(self, level, tree, msg): #
# if isinstance(msg, tuple): # def display_warning(self, level, tree, msg):
# msg = f"{msg[0]}: [{self.warning_color}]:{self.warning_icon}: {msg[1]}[/{self.warning_color}]" # if isinstance(msg, tuple):
# return tree.add(msg, guide_style=f"bold {self.warning_color}") # msg = f"{msg[0]}: [{self.warning_color}]:{self.warning_icon}: {msg[1]}[/{self.warning_color}]"
# return tree.add(msg, guide_style=f"bold {self.warning_color}")
def add_variable( def add_variable(
self, parent, description, value, icon, level, self,
parent,
description,
value,
icon,
level,
): ):
if icon == 'leaf': if icon == "leaf":
icon = 'notebook' icon = "notebook"
else: else:
icon = 'open_file_folder' icon = "open_file_folder"
if isinstance(value, list): if isinstance(value, list):
subtree = parent.add( subtree = parent.add(
f":{icon}: " + _("{0}:").format(description), f":{icon}: " + _("{0}:").format(description),
@ -150,13 +165,13 @@ class OutputFamily(CommonOutput):
return msg return msg
return f"[{color}]{msg}[/{color}]" return f"[{color}]{msg}[/{color}]"
def get_parent(self, parent, description, level): def get_parent(self, parent, description, level):
if parent is None: if parent is None:
return Tree(description, return Tree(
guide_style=self.guide_style, description,
) guide_style=self.guide_style,
)
return parent.add( return parent.add(
f":open_file_folder: {description}", f":open_file_folder: {description}",
guide_style=self.guide_style, guide_style=self.guide_style,
) )

View file

@ -1,7 +1,7 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2025-2026 Copyright (C) 2025-2026
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
Free Software Foundation, either version 3 of the License, or (at your Free Software Foundation, either version 3 of the License, or (at your
@ -42,12 +42,18 @@ class OutputFamily(CommonOutput):
if self.variable_default_enable: if self.variable_default_enable:
variables.append(_("Variable")) variables.append(_("Variable"))
if self.variable_hidden_enable: if self.variable_hidden_enable:
variables.append(self.set_color(self.variable_hidden_color, _("Unmodifiable variable"))) variables.append(
self.set_color(self.variable_hidden_color, _("Unmodifiable variable"))
)
values = [] values = []
if self.value_unmodified_enable: if self.value_unmodified_enable:
values.append(self.set_color(self.value_unmodified_color, _("Default value"))) values.append(
self.set_color(self.value_unmodified_color, _("Default value"))
)
if self.value_modified_enable: if self.value_modified_enable:
values.append(self.set_color(self.value_modified_color, _("Modified value"))) values.append(
self.set_color(self.value_modified_color, _("Modified value"))
)
if self.value_default_enable: if self.value_default_enable:
values.append(f'(:hourglass_flowing_sand: {_("Original default value")})') values.append(f'(:hourglass_flowing_sand: {_("Original default value")})')
if not variables and not values: if not variables and not values:
@ -68,14 +74,14 @@ class OutputFamily(CommonOutput):
def title(self, msg): def title(self, msg):
caption = "> [!NOTE]" + "\n>\n" caption = "> [!NOTE]" + "\n>\n"
caption += f'> **{msg}**\n' caption += f"> **{msg}**\n"
return caption return caption
def error_header(self): def error_header(self):
return ['> [!CAUTION]\n> '] return ["> [!CAUTION]\n> "]
def warning_header(self): def warning_header(self):
return ['> [!WARNING]\n> '] return ["> [!WARNING]\n> "]
def error_end(self): def error_end(self):
self.out[-1] += "\n" self.out[-1] += "\n"
@ -93,7 +99,7 @@ class OutputFamily(CommonOutput):
color = self.warning_color color = self.warning_color
if isinstance(msg, tuple): if isinstance(msg, tuple):
if len(msg) == 3: if len(msg) == 3:
if msg[2] == 'error': if msg[2] == "error":
color = self.error_color color = self.error_color
icon = self.error_icon icon = self.error_icon
else: else:
@ -101,7 +107,7 @@ class OutputFamily(CommonOutput):
icon = self.warning_icon icon = self.warning_icon
msg = f"{msg[0]}: :{icon}: {self.set_color(color, msg[1])}" msg = f"{msg[0]}: :{icon}: {self.set_color(color, msg[1])}"
else: else:
if msg[1] == 'error': if msg[1] == "error":
icon = self.error_icon icon = self.error_icon
else: else:
color = self.warning_color color = self.warning_color
@ -111,17 +117,22 @@ class OutputFamily(CommonOutput):
return tree return tree
def add_variable( def add_variable(
self, parent, description, value, icon, level, self,
parent,
description,
value,
icon,
level,
): ):
if parent is None: if parent is None:
parent = [] parent = []
if icon == 'leaf': if icon == "leaf":
icon = 'notebook' icon = "notebook"
else: else:
icon = 'open_file_folder' icon = "open_file_folder"
before = " " * (level - 1) * 2 + "- " before = " " * (level - 1) * 2 + "- "
if isinstance(value, list): if isinstance(value, list):
description = description.replace('', '') description = description.replace("", "")
subtree = parent.append( subtree = parent.append(
f"{before}:{icon}: " + _("{0}:").format(description), f"{before}:{icon}: " + _("{0}:").format(description),
) )
@ -129,7 +140,9 @@ class OutputFamily(CommonOutput):
for val in value: for val in value:
parent.append(before + str(val)) parent.append(before + str(val))
else: else:
parent.append(before + f":{icon}: " + _("{0}: {1}").format(description, value)) parent.append(
before + f":{icon}: " + _("{0}: {1}").format(description, value)
)
def set_color(self, color, msg): def set_color(self, color, msg):
if not color: if not color:
@ -150,5 +163,7 @@ class OutputFamily(CommonOutput):
parent = [] parent = []
parent.append(description) parent.append(description)
else: else:
parent.append(" " * (level - 1) * 2 + "- " + ":open_file_folder: " + description) parent.append(
" " * (level - 1) * 2 + "- " + ":open_file_folder: " + description
)
return parent return parent

View file

@ -1,7 +1,7 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2025-2026 Copyright (C) 2025-2026
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
Free Software Foundation, either version 3 of the License, or (at your Free Software Foundation, either version 3 of the License, or (at your
@ -32,7 +32,7 @@ class OutputFamily(GHOutputFamily):
def set_color(self, color, msg): def set_color(self, color, msg):
if not color: if not color:
return msg return msg
return f'[{color} {msg} {color}]' return f"[{color} {msg} {color}]"
def title(self, msg): def title(self, msg):
return f"> [!note] {msg}" + "\n>\n" return f"> [!note] {msg}" + "\n>\n"

View file

@ -1,7 +1,7 @@
""" """
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2026 Copyright (C) 2022-2026
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
Free Software Foundation, either version 3 of the License, or (at your Free Software Foundation, either version 3 of the License, or (at your
@ -15,6 +15,7 @@ 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/>.
""" """
from typing import Any, Optional from typing import Any, Optional
# from tiramisu import owners # from tiramisu import owners
@ -28,7 +29,7 @@ class CommonOutput:
def __init__( def __init__(
self, self,
rougailconfig, rougailconfig,
) -> None: ) -> None:
self.set_config(rougailconfig) self.set_config(rougailconfig)
self.variable_default_enable = False self.variable_default_enable = False
self.variable_hidden_enable = False self.variable_hidden_enable = False
@ -44,7 +45,7 @@ class CommonOutput:
if nodes.children: if nodes.children:
root = self.parse(nodes) root = self.parse(nodes)
return self._run(root) return self._run(root)
return 0, '' return 0, ""
def parse(self, node, parent=None, level=0): def parse(self, node, parent=None, level=0):
if node.hidden: if node.hidden:
@ -55,18 +56,18 @@ class CommonOutput:
self.variable_default_enable = True self.variable_default_enable = True
color = self.variable_normal_color color = self.variable_normal_color
family_output = self.colorize( family_output = self.colorize(
[ [
{ {
"value": node.description, "value": node.description,
"color": color, "color": color,
"loaded_from": None, "loaded_from": None,
} }
] ]
) )
subparent = self.get_parent(parent, family_output, level) subparent = self.get_parent(parent, family_output, level)
for child in node.children: for child in node.children:
if child["type"] == "node": if child["type"] == "node":
self.parse(child["node"], subparent, level+1) self.parse(child["node"], subparent, level + 1)
else: else:
if child["hidden"]: if child["hidden"]:
self.variable_hidden_enable = True self.variable_hidden_enable = True
@ -87,15 +88,17 @@ class CommonOutput:
child["values"], child["values"],
) )
description = self.colorize( description = self.colorize(
[ [
{ {
"value": child["description"], "value": child["description"],
"color": variable_color, "color": variable_color,
"loaded_from": None, "loaded_from": None,
} }
] ]
) )
self.add_variable(subparent, description, value, child["icon"], level+1) self.add_variable(
subparent, description, value, child["icon"], level + 1
)
return subparent return subparent
def header(self): def header(self):
@ -105,12 +108,14 @@ class CommonOutput:
if isinstance(error, dict): if isinstance(error, dict):
for msg, subconfig in error.items(): for msg, subconfig in error.items():
description = subconfig.option.impl_get_display_name(subconfig) description = subconfig.option.impl_get_display_name(subconfig)
self.subconfig_to_dict(subconfig, errors_dict).setdefault(None, {}).setdefault(description, []).append((msg, level)) self.subconfig_to_dict(subconfig, errors_dict).setdefault(
None, {}
).setdefault(description, []).append((msg, level))
else: else:
errors_dict.setdefault(None, []).append(error) errors_dict.setdefault(None, []).append(error)
def subconfig_to_dict(self, subconfig: "Subconfig", errors_dict: dict) -> dict: def subconfig_to_dict(self, subconfig: "Subconfig", errors_dict: dict) -> dict:
#FIXME a tester : mandatories dans une arborescence (voir si ca n'ecrase pas) # FIXME a tester : mandatories dans une arborescence (voir si ca n'ecrase pas)
parents = [] parents = []
parent = subconfig parent = subconfig
while True: while True:
@ -131,18 +136,26 @@ class CommonOutput:
def _parse_error_warning(self, tree, error, display, level, default_color): def _parse_error_warning(self, tree, error, display, level, default_color):
if isinstance(error, list): if isinstance(error, list):
for err in error: for err in error:
self._parse_error_warning(tree, err, display, level+1, default_color) self._parse_error_warning(tree, err, display, level + 1, default_color)
elif isinstance(error, dict): elif isinstance(error, dict):
for key, value in error.items(): for key, value in error.items():
if key is None: if key is None:
# it's variables, no more families # it's variables, no more families
self._parse_error_warning(tree, value, display, level, default_color) self._parse_error_warning(
tree, value, display, level, default_color
)
else: else:
if isinstance(value, list) and len(value) == 1: if isinstance(value, list) and len(value) == 1:
self._parse_error_warning(tree, (key, *value[0]), display, level+1, default_color) self._parse_error_warning(
tree, (key, *value[0]), display, level + 1, default_color
)
else: else:
sub_tree = self._parse_error_warning(tree, key, display, level+1, default_color) sub_tree = self._parse_error_warning(
self._parse_error_warning(sub_tree, value, display, level+1, default_color) tree, key, display, level + 1, default_color
)
self._parse_error_warning(
sub_tree, value, display, level + 1, default_color
)
else: else:
return display(level, tree, error, default_color) return display(level, tree, error, default_color)
@ -153,7 +166,7 @@ class CommonOutput:
self, self,
family, family,
level, level,
) -> 'OutputFamily': ) -> "OutputFamily":
properties = family.property.get() properties = family.property.get()
if "hidden" in properties: if "hidden" in properties:
self.root_family.variable_hidden_enable = True self.root_family.variable_hidden_enable = True
@ -162,20 +175,20 @@ class CommonOutput:
self.root_family.variable_default_enable = True self.root_family.variable_default_enable = True
color = None color = None
family_output = self.colorize( family_output = self.colorize(
[ [
{ {
"value": family.description(), "value": family.description(),
"color": color, "color": color,
"loaded_from": None, "loaded_from": None,
} }
] ]
) )
return self.__class__( return self.__class__(
family_output, family_output,
self.get_tree(), self.get_tree(),
self.root, self.root,
level + 1, level + 1,
root_family = self.root_family, root_family=self.root_family,
) )
def colorize( def colorize(

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,12 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,16 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,13 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
 ┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,16 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,13 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
 ┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [+ Default value +]
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [+ val1 +]
- [+ val2 +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #B8860B">Default value</span>
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #B8860B">val1</span>
- <span style="color: #B8860B">val2</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>

View file

@ -0,0 +1,12 @@
╭─────── Caption ────────╮
│ Variable Default value │
╰────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1
 ┃ ┗━━ val2
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: null
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: null

View file

@ -0,0 +1,15 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,16 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,13 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
 ┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [+ Default value +]
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [+ val1 +]
- [+ val2 +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #B8860B">Default value</span>
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #B8860B">val1</span>
- <span style="color: #B8860B">val2</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>

View file

@ -0,0 +1,12 @@
╭─────── Caption ────────╮
│ Variable Default value │
╰────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1
 ┃ ┗━━ val2
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: null
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: null

View file

@ -0,0 +1,15 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,16 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :open_file_folder: Rougail
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,13 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┗━━ 📂 Rougail
 ┣━━ 📓 A suffix variable:
 ┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
 ┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
 ┣━━ 📂 A dynamic family
 ┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
 ┗━━ 📂 A dynamic family
  ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,12 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,13 @@
> [!note] Caption:
>
> - Variable
> - [+ Default value +]
Variables:
- :notebook: A suffix variable:
- [+ val1 +]
- [+ val2 +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]

View file

@ -0,0 +1,14 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #B8860B">Default value</span>
Variables:
- :notebook: A suffix variable:
- <span style="color: #B8860B">val1</span>
- <span style="color: #B8860B">val2</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>

View file

@ -0,0 +1,11 @@
╭─────── Caption ────────╮
│ Variable Default value │
╰────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1
┃ ┗━━ val2
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: null
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: null

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,12 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test

View file

@ -0,0 +1,13 @@
> [!note] Caption:
>
> - Variable
> - [+ Default value +]
Variables:
- :notebook: A suffix variable:
- [+ val1 +]
- [+ val2 +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [+ null +]

View file

@ -0,0 +1,14 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #B8860B">Default value</span>
Variables:
- :notebook: A suffix variable:
- <span style="color: #B8860B">val1</span>
- <span style="color: #B8860B">val2</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #B8860B">null</span>

View file

@ -0,0 +1,11 @@
╭─────── Caption ────────╮
│ Variable Default value │
╰────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1
┃ ┗━━ val2
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: null
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: null

View file

@ -0,0 +1,14 @@
> [!note] Caption:
>
> - Variable
> - [- Modified value -]
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- [- val1 -] ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- [- val2 -] ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: [- string1 -] ← loaded from rougail-test

View file

@ -0,0 +1,15 @@
> [!NOTE]
>
> **Caption:**
> - Variable
> - <span style="color: #006400">Modified value</span>
> - (:hourglass_flowing_sand: Original default value)
Variables:
- :notebook: A suffix variable:
- <span style="color: #006400">val1</span> ← loaded from rougail-test (:hourglass_flowing_sand: val1)
- <span style="color: #006400">val2</span> ← loaded from rougail-test (:hourglass_flowing_sand: val2)
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test
- :open_file_folder: A dynamic family
- :notebook: A dynamic variable: <span style="color: #006400">string1</span> ← loaded from rougail-test

View file

@ -0,0 +1,12 @@
╭────────────── Caption ───────────────╮
│ Variable Modified value │
(⏳ Original default value)
╰──────────────────────────────────────╯
Variables:
┣━━ 📓 A suffix variable:
┃ ┣━━ val1 ◀ loaded from rougail-test (⏳ val1)
┃ ┗━━ val2 ◀ loaded from rougail-test (⏳ val2)
┣━━ 📂 A dynamic family
┃ ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test
┗━━ 📂 A dynamic family
 ┗━━ 📓 A dynamic variable: string1 ◀ loaded from rougail-test