feat: start user_datas/output documentation

This commit is contained in:
egarette@silique.fr 2025-10-26 22:21:21 +01:00
parent 4bb50361d5
commit 2eeedd70e2
4 changed files with 548 additions and 8 deletions

View file

@ -73,6 +73,8 @@ The Rougail CLI can output a rather complete view of the dataset:
:titlesonly:
:caption: Use library
user_datas
output
parse
tags
rougailconfig_load_from_cli

144
docs/library/output.rst Normal file
View file

@ -0,0 +1,144 @@
Display the result
==================
After construct a configuration, loads user datas, you can choose this configuration in different output format.
First of create, let's create a structural file like this:
.. code-block:: yaml
:caption: the :file:`dist/00-base.yml` file content
%YAML 1.2
---
version: 1.1
my_variable: my value # My first variable
my_boolean_variable: true # My boolean variable
my_integer_variable: 1 # My integer variable
my_secret_variable:
description: My secret variable
type: secret
default: MyVeryStrongPassword
...
Display in a console
--------------------
We can display configuration directly in the console:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.output_console import RougailOutputConsole
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.output"] = "console"
rougail = Rougail(RougailConfig)
config = rougail.run()
config.property.read_only()
RougailOutputConsole(config).print()
FIXME display console!
console.key_is_description
''''''''''''''''''''''''''
By default, the key is the variable description, if you prefer have only the path:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.output_json import RougailOutputJson
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.output"] = "json"
rougail = Rougail(RougailConfig)
config = rougail.run()
config.property.read_only()
RougailOutputJson(config).print()
FIXME display console!
console.show_secrets
''''''''''''''''''''
Secrets are remplace by "*******", to display real secrets:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.output_console import RougailOutputConsole
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["console.show_secrets"] = True
rougail = Rougail(RougailConfig)
config = rougail.run()
config.property.read_only()
RougailOutputConsole(config).print()
FIXME display console!
console.mandatory
'''''''''''''''''
Before display configuration, mandatories variables are check. If you don't want, add the parameter `console.mandatory` to False:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.output_console import RougailOutputConsole
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["console.mandatory"] = False
rougail = Rougail(RougailConfig)
config = rougail.run()
config.property.read_only()
RougailOutputConsole(config).print()
FIXME display console!
JSON
----
Your script can return a JSON object:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.output_console import RougailOutputConsole
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["console.mandatory"] = False
rougail = Rougail(RougailConfig)
config = rougail.run()
config.property.read_only()
RougailOutputConsole(config).print()
Let's try this script:
.. code-block:: bash
$ LC_ALL=C python script.py
{
"my_variable": "my value",
"my_boolean_variable": true,
"my_integer_variable": 1,
"my_secret_variable": "MyVeryStrongPassword"
}
.. doc,ansible

View file

@ -12,7 +12,7 @@ First of all, create our structural file:
%YAML 1.2
---
version: 1.1
version: 1.1
my_variable: # a simple variable
- value1
@ -43,17 +43,17 @@ Create our first script to walk through our config:
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
RougailConfig['main_structural_directories'] = ['dist']
rougail = Rougail()
config = rougail.run()
def walk(config):
for option in config:
print(option.description())
if option.isoptiondescription():
walk(option)
if __name__ == '__main__':
walk(config)
@ -61,7 +61,7 @@ Let's execute `script.py`:
.. code-block:: bash
$ python3 script.py
$ python3 script.py
rougail
rougail.my_variable (a simple variable)
rougail.a_family (a simple family)
@ -84,11 +84,11 @@ Let us distinguish the variables of the families
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
RougailConfig['main_structural_directories'] = ['dist']
rougail = Rougail()
config = rougail.run()
def walk(config, level=0):
for option in config:
if option.isoptiondescription():
@ -99,7 +99,7 @@ Let us distinguish the variables of the families
print(f"{prefix}{typ}: {option.description()}")
if option.isoptiondescription():
walk(option, level + 1)
if __name__ == '__main__':
walk(config)
@ -107,6 +107,7 @@ Let's execute `script.py`:
.. code-block:: bash
$ python3 script.py
family: rougail
variable: rougail.my_variable (a simple variable)
family: rougail.a_family (a simple family)

393
docs/library/user_datas.rst Normal file
View file

@ -0,0 +1,393 @@
Load user datas
===============
User datas are values setup but user for configuration variables.
There is differents types of user datas for differents sources types.
We can cumulate user datas loader.
For this section, we will use :file:`dict/00-base.yml` a structure file:
.. code-block:: yaml
%YAML 1.2
---
version: 1.1
my_variable: my value # My first variable
my_boolean_variable: true # My boolean variable
my_integer_variable: 1 # My integer variable
my_secret_variable:
description: My secret variable
type: secret
my_hidden_variable:
description: My hidden variable
hidden: true
...
Here is the first script which is load this file:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
rougail = Rougail()
config = rougail.run()
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
$ python3 script.py
{<TiramisuOption path="my_variable">: 'my value', <TiramisuOption path="my_boolean_variable">: True, <TiramisuOption path="my_integer_variable">: 1, <TiramisuOption path="my_secret_variable">: None}
YAML
----
We want to load this YAML file with value define by user:
.. code-block:: yaml
---
my_variable: a new value
my_boolean_variable: false
my_integer_variable: 10
my_secret_variable: MyVeryStrongPassword
Here is the script which is load user data from the YAML file:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_yaml import RougailUserDataYaml
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["yaml"]
RougailConfig["yaml.filename"] = ["dist.yml"]
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataYaml(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
$ python3 script.py
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
Set a secret in clear text file is not always a good idea.
This is why the `yaml.file_with_secrets` parameter allows you to define whether files define in `yaml.filename` can contain a secret and which one:
- all: all file can contains secret
- first: only the first file can contains secret
- last: only the last file can contains secret
- none: no file can contains secret
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_yaml import RougailUserDataYaml
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["yaml"]
RougailConfig["yaml.filename"] = ["dist.yml"]
RougailConfig["yaml.file_with_secrets"] = "none"
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataYaml(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
$ python3 script.py
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: None}
Environment variables
---------------------
We can define use data from environment variables. The environment name is a "prefix" (ROUGAIL by default) with "_" and variable name in uppercase format.
For example:
- my_variable has ROUGAIL_MY_VARIABLE as a environment variable name
- my_family.my_variable has ROUGAIL_MY_FAMILY.MY_VARIABLE as a environment variable name
Some shell doesn't allow dot in environment file. In this case use the command "env".
For example: `env ROUGAIL_MY_FAMILY.MY_VARIABLE="value" ./script.py`.
Here is the script:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment"]
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataEnvironment(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
env ROUGAIL_MY_VARIABLE="a new value" ROUGAIL_MY_BOOLEAN_VARIABLE="False" ROUGAIL_MY_INTEGER_VARIABLE=10 ROUGAIL_MY_SECRET_VARIABLE="MyVeryStrongPassword" python script.py
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
We can redefine the prefix with `environment.default_environment_name` (prefix is always uppercase characters):
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment"]
RougailConfig["environment.default_environment_name"] = "EX"
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataEnvironment(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
env EX_MY_VARIABLE="a new value" EX_MY_BOOLEAN_VARIABLE="False" EX_MY_INTEGER_VARIABLE=10 EX_MY_SECRET_VARIABLE="MyVeryStrongPassword" python script.py
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
If you define a `main_namespace` or `extra_namespace`, the `environment.default_environment_name` is automaticly define with the name of the namespace in uppercase. And the separator is no more "_" but ".":
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
RougailConfig["main_namespace"] = "main"
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment"]
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataEnvironment(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
env MAIN.MY_VARIABLE="a new value" MAIN.MY_BOOLEAN_VARIABLE="False" MAIN.MY_INTEGER_VARIABLE=10 MAIN.MY_SECRET_VARIABLE="MyVeryStrongPassword" python script.py
{<TiramisuOption path="main">: {<TiramisuOption path="main.my_variable">: 'a new value', <TiramisuOption path="main.my_boolean_variable">: False, <TiramisuOption path="main.my_integer_variable">: 10, <TiramisuOption path="main.my_secret_variable">: 'MyVeryStrongPassword'}}
Set a secret in clear variable environment is not always a good idea.
This is why the `environment.with_secrets` parameter allows you to reject secret from environment variable:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment"]
RougailConfig["environment.with_secrets"] = False
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataEnvironment(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py`:
.. code-block:: bash
env ROUGAIL_MY_VARIABLE="a new value" ROUGAIL_MY_BOOLEAN_VARIABLE="False" ROUGAIL_MY_INTEGER_VARIABLE=10 ROUGAIL_MY_SECRET_VARIABLE="MyVeryStrongPassword" python script.py
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: None}
Comand line parser user data
----------------------------
Value can be define directly with command line arguments:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_commandline import RougailUserDataCommandline
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["commandline"]
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataCommandline(
config,
).run()
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py` to display help:
.. code-block:: bash
$ python script.py -h
usage: script.py [-h] --my_variable [MY_VARIABLE] --my_boolean_variable --no-my_boolean_variable --my_integer_variable [MY_INTEGER_VARIABLE] --my_secret_variable MY_SECRET_VARIABLE
options:
-h, --help show this help message and exit
--my_variable [MY_VARIABLE]
my_variable (My first variable) (default: my value)
--my_boolean_variable
my_boolean_variable (My boolean variable) (default: True)
--no-my_boolean_variable
--my_integer_variable [MY_INTEGER_VARIABLE]
my_integer_variable (My integer variable) (default: 1)
--my_secret_variable MY_SECRET_VARIABLE
my_secret_variable (My secret variable)
{<TiramisuOption path="my_variable">: 'my value', <TiramisuOption path="my_boolean_variable">: True, <TiramisuOption path="my_integer_variable">: 1, <TiramisuOption path="my_secret_variable">: None}
And now with modified value:
.. code-block:: bash
$ python script.py --my_variable "a new value" --no-my_boolean_variable --my_integer_variable 10 --my_secret_variable MyVeryStrongPassword
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
Boolean variable has a special behavour. To set False you need to add --no-VARIABLE, to set True you need to add --VARIABLE parameter.
.. ansible,bitwarden,questionary
Combine user datas
------------------
You can combine user datas, for example if you want to load datas from environment and/or command line argument:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
from rougail.user_data_commandline import RougailUserDataCommandline
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment", "commandline"]
rougail = Rougail()
config = rougail.run()
user_datas = []
user_datas.extend(RougailUserDataEnvironment(
config,
).run())
user_datas.extend(RougailUserDataCommandline(
config,
).run())
rougail.user_datas(user_datas)
print(config.value.get())
Let's execute `script.py` with environment variable and commandline arguments:
.. code-block:: bash
$ env ROUGAIL_MY_VARIABLE="a new value" ROUGAIL_MY_BOOLEAN_VARIABLE="False" python script.py --my_integer_variable 10 --my_secret_variable MyVeryStrongPassword
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
If the value of a variable is define with an environment variable and commandline argument, the value is the value of the last user data define:
.. code-block:: bash
$ env ROUGAIL_MY_VARIABLE="not a new" python script.py --my_variable "a new value" --no-my_boolean_variable --my_integer_variable 10 --my_secret_variable MyVeryStrongPassword
{<TiramisuOption path="my_variable">: 'a new value', <TiramisuOption path="my_boolean_variable">: False, <TiramisuOption path="my_integer_variable">: 10, <TiramisuOption path="my_secret_variable">: 'MyVeryStrongPassword'}
Manage errors and warnings
--------------------------
Recreate a script with environnement variable support which is display the return of user_datas function:
.. code-block:: python
:caption: the :file:`script.py` file content
from rougail import Rougail, RougailConfig
from rougail.user_data_environment import RougailUserDataEnvironment
RougailConfig["main_namespace"] = None
RougailConfig["main_structural_directories"] = ["dist"]
RougailConfig["step.user_data"] = ["environment"]
RougailConfig["environment.with_secrets"] = False
rougail = Rougail()
config = rougail.run()
user_datas = RougailUserDataEnvironment(
config,
).run()
print(rougail.user_datas(user_datas))
Try to load the value an unknown variable:
.. code-block:: bash
$ LC_ALL=C env ROUGAIL_UNKNOWN_VARIABLE="a value" python script.py
{'errors': [], 'warnings': ['variable or family "unknown_variable" does not exist, it will be ignored when loading from environment variable']}
As you can see, a warnings is return.
Try to load the value of an hidden variable:
.. code-block:: bash
$ LC_ALL=C env ROUGAIL_MY_HIDDEN_VARIABLE="a value" python script.py
{'errors': [], 'warnings': ['variable "my_hidden_variable" (My hidden variable) is hidden, it will be ignored when loading from environment variable']}
Finally if a try to change the value of a secret, which is not allowed:
.. code-block:: bash
$ LC_ALL=C env ROUGAIL_MY_SECRET_VARIABLE="MyVeryStrongPassword" python script.py
{'errors': ['the variable "my_secret_variable" contains secrets and should not be defined in environment variable'], 'warnings': []}
An error is generate.