Compare commits
13 commits
main
...
developer_
Author | SHA1 | Date | |
---|---|---|---|
|
620f09a041 | ||
|
d5661511da | ||
|
b6a814fbc0 | ||
|
ad0e809bc9 | ||
|
d2527f6353 | ||
5668520a31 | |||
e0e6597955 | |||
13aedef773 | |||
59d12a3624 | |||
|
bc4420bdea | ||
0d66bffd2f | |||
887f8aea9d | |||
f1a8f61347 |
1591 changed files with 7561 additions and 24165 deletions
47
.pre-commit-config.yaml
Normal file
47
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v2.3.0
|
||||||
|
hooks:
|
||||||
|
- id: check-yaml
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: check-ast
|
||||||
|
- id: check-added-large-files
|
||||||
|
- id: check-json
|
||||||
|
- id: check-executables-have-shebangs
|
||||||
|
- id: check-symlinks
|
||||||
|
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 22.10.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
|
||||||
|
# - repo: https://github.com/hhatto/autopep8
|
||||||
|
# rev: v2.0.4
|
||||||
|
# hooks:
|
||||||
|
# - id: autopep8
|
||||||
|
|
||||||
|
# - repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
# rev: v1.6.1
|
||||||
|
# hooks:
|
||||||
|
# - id: mypy
|
||||||
|
|
||||||
|
# - repo: https://github.com/PyCQA/pylint
|
||||||
|
# rev: v3.0.2
|
||||||
|
# hooks:
|
||||||
|
# - id: pylint
|
||||||
|
|
||||||
|
# - repo: https://github.com/PyCQA/isort
|
||||||
|
# rev: 5.11.5
|
||||||
|
# hooks:
|
||||||
|
# - id: isort
|
||||||
|
|
||||||
|
# - repo: https://github.com/motet-a/jinjalint
|
||||||
|
# rev: 0.5
|
||||||
|
# hooks:
|
||||||
|
# - id: jinjalint
|
||||||
|
|
||||||
|
# - repo: https://github.com/rstcheck/rstcheck
|
||||||
|
# rev: v6.2.0
|
||||||
|
# hooks:
|
||||||
|
# - id: rstcheck
|
24
docs/Makefile
Normal file
24
docs/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
# Live reload site documents for local development
|
||||||
|
livehtml:
|
||||||
|
sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -117,7 +117,7 @@ Here is a simple example of validating values:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
validators:
|
validators:
|
||||||
- type: jinja
|
- type: jinja
|
||||||
|
@ -142,7 +142,7 @@ In the constraint, it is possible to specify the error level and put it as a war
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
validators:
|
validators:
|
||||||
- type: jinja
|
- type: jinja
|
||||||
|
@ -161,7 +161,7 @@ Verification with parameters
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_hidden_variable:
|
my_hidden_variable:
|
||||||
disabled: true
|
disabled: true
|
||||||
my_variable:
|
my_variable:
|
||||||
|
@ -189,7 +189,7 @@ An example with a suffix type parameter:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -221,7 +221,7 @@ An example with an index type parameter:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
family:
|
family:
|
||||||
type: leadership
|
type: leadership
|
||||||
leader:
|
leader:
|
||||||
|
@ -249,7 +249,7 @@ In a first dictionary, let's declare our variable and its verification function:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
validators:
|
validators:
|
||||||
- type: jinja
|
- type: jinja
|
||||||
|
@ -263,7 +263,7 @@ In a second dictionary it is possible to redefine the calculation:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
redefine: true
|
redefine: true
|
||||||
validators:
|
validators:
|
||||||
|
@ -280,7 +280,7 @@ Here is a third dictionary in which we remove the validation:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
redefine: true
|
redefine: true
|
||||||
validators:
|
validators:
|
||||||
|
|
|
@ -177,7 +177,7 @@ It is possible to write the condition in Jinja:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
condition:
|
condition:
|
||||||
default: 'do not hide!'
|
default: 'do not hide!'
|
||||||
my_variable:
|
my_variable:
|
||||||
|
@ -213,7 +213,7 @@ A variable can therefore be calculated via the result of another variable. Pleas
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
condition:
|
condition:
|
||||||
type: boolean
|
type: boolean
|
||||||
my_variable:
|
my_variable:
|
||||||
|
@ -233,7 +233,7 @@ To delete the calculation from a variable, simply do in a new dictionary:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
redefine: true
|
redefine: true
|
||||||
hidden:
|
hidden:
|
||||||
|
|
|
@ -5,12 +5,199 @@ Developer notes
|
||||||
|
|
||||||
This section is intended to be usefull for team developers only.
|
This section is intended to be usefull for team developers only.
|
||||||
|
|
||||||
|
Developer installation process
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
Quick installation process
|
The conventional installation process is as usual with the python installer, that is::
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
This process describes how to install and run the project locally, e.g. for development purposes.
|
pip install rougail
|
||||||
|
|
||||||
*Nota*: command is to be executed through the terminal
|
This section describes how to install and run the project locally, e.g. for development purposes,
|
||||||
|
so we won't use this installation way, we will clone `rougail` directly from the repository.
|
||||||
|
The best way to work with the library in development mode is to install a virtual
|
||||||
|
environment::
|
||||||
|
|
||||||
`pip install rougail`
|
python -m"venv" .venv
|
||||||
|
|
||||||
|
then install the dependencies::
|
||||||
|
|
||||||
|
./.venv/bin/pip3 install ruamel.yaml
|
||||||
|
./.venv/bin/pip3 install pydantic
|
||||||
|
./.venv/bin/pip3 install jinja2
|
||||||
|
|
||||||
|
.. note:: Please have a look at the `pyproject.toml` install configuration file.
|
||||||
|
There is a `dependencies` section.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
"ruamel.yaml ~= 0.17.40",
|
||||||
|
"pydantic ~= 2.5.2",
|
||||||
|
"jinja2 ~= 3.1.2",
|
||||||
|
"tiramisu ~= 4.1.0"
|
||||||
|
]
|
||||||
|
|
||||||
|
Then we need to install two libraries
|
||||||
|
|
||||||
|
- rougail::
|
||||||
|
|
||||||
|
git clone https://forge.cloud.silique.fr/stove/rougail.git
|
||||||
|
|
||||||
|
- tiramisu::
|
||||||
|
|
||||||
|
git clone https://forge.cloud.silique.fr/stove/tiramisu.git
|
||||||
|
|
||||||
|
The we put it in the :envvar:`PATH`::
|
||||||
|
|
||||||
|
export PYTHONPATH=$PYTHONPATH:'./tiramisu/'
|
||||||
|
export PYTHONPATH=$PYTHONPATH:'./rougail/src'
|
||||||
|
|
||||||
|
You can put these two line in a bash script, named for example :file:`export_path.sh`,
|
||||||
|
then source it::
|
||||||
|
|
||||||
|
source export_path.sh
|
||||||
|
|
||||||
|
To run rougail and see that everything is doing well, you can use the
|
||||||
|
:ref:`hello world sample script <helloworld>`.
|
||||||
|
|
||||||
|
Code quality
|
||||||
|
---------------
|
||||||
|
|
||||||
|
We are using `pre-commit <https://pre-commit.com/>`_, there is a :file:`.pre-commit-config.yaml`
|
||||||
|
pre-commit config file in the root's project.
|
||||||
|
|
||||||
|
You need to:
|
||||||
|
|
||||||
|
- install the pre-commit library::
|
||||||
|
|
||||||
|
pip install pre-commit
|
||||||
|
|
||||||
|
- registrer the pre-commit git hooks with this command::
|
||||||
|
|
||||||
|
pre-commit install
|
||||||
|
|
||||||
|
- launch the quality code procedure with::
|
||||||
|
|
||||||
|
pre-commit
|
||||||
|
|
||||||
|
or simply just commit your changes, pre-commit will automatically be launched.
|
||||||
|
|
||||||
|
.. attention:: If an error is found, the commit will not happen.
|
||||||
|
You must resolve all errors that pre-commit that pre-commit points out to you before.
|
||||||
|
|
||||||
|
.. note:: If you need for some reason to disable `pre-commit`, just set
|
||||||
|
the `PRE_COMMIT_ALLOW_NO_CONFIG` environment variable before commiting::
|
||||||
|
|
||||||
|
PRE_COMMIT_ALLOW_NO_CONFIG=1 git commit
|
||||||
|
|
||||||
|
Coding standard
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Pre-commit launches black and pep8 validators.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 22.10.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
|
||||||
|
And some YAML and JSON validators.
|
||||||
|
|
||||||
|
Test framework
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Our test framework lives in the `tests` project's folder.
|
||||||
|
|
||||||
|
You need to install the pytest tool::
|
||||||
|
|
||||||
|
./.venv/bin/pip3 install pytest
|
||||||
|
|
||||||
|
Here is a sample test session::
|
||||||
|
|
||||||
|
../.venv/bin/pytest -s tests/test_1_flattener.py
|
||||||
|
==================== test session starts =====================
|
||||||
|
platform linux -- Python 3.12.3, pytest-8.2.2, pluggy-1.5.0
|
||||||
|
rootdir: /home/ubuntu/workplace/rougail
|
||||||
|
configfile: pyproject.toml
|
||||||
|
collected 586 items
|
||||||
|
|
||||||
|
tests/test_1_flattener.py ............................................
|
||||||
|
================== short test summary info ===================
|
||||||
|
FAILED tests/test_1_flattener.py
|
||||||
|
FAILED tests/test_1_flattener.py
|
||||||
|
========== 2 failed, 584 passed, 1 warning in 5.69s ==========
|
||||||
|
|
||||||
|
.. note:: For the moment, the tests must be lauched at the `rougail`'s root project.
|
||||||
|
|
||||||
|
The tests that live in these files:
|
||||||
|
|
||||||
|
- `test_1_flattener.py`
|
||||||
|
- `test_1_flattener.py`
|
||||||
|
|
||||||
|
Are "automatic" tests, that is
|
||||||
|
|
||||||
|
- they run `fixtures in the pytest way <https://docs.pytest.org/en/7.1.x/how-to/fixtures.html>`_
|
||||||
|
- they require a specific tree structure
|
||||||
|
|
||||||
|
The tree structure is in the `rougail/tests/dictionaries` folder, you need to
|
||||||
|
create a folder that start with a specific test code number, for example::
|
||||||
|
|
||||||
|
80no_leadership_index2
|
||||||
|
80no_version
|
||||||
|
...
|
||||||
|
80unknown_type
|
||||||
|
80unsupported_version
|
||||||
|
80valid_enum_multi_param
|
||||||
|
80valid_enum_multi_variable
|
||||||
|
|
||||||
|
inside theses folders, the directory tree structure shall always be the same::
|
||||||
|
|
||||||
|
__init__.py dictionaries makedict tiramisu
|
||||||
|
|
||||||
|
|
||||||
|
To add a new test, you use a script that finds the test code already in use
|
||||||
|
like this one:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
:caption: The :file:`find_id.sh` script
|
||||||
|
|
||||||
|
for i in $(seq 1 100); do
|
||||||
|
grep -qRI "$i," src/rougail/
|
||||||
|
if [ ! "$?" = "0" ]; then
|
||||||
|
echo "======================== $i ============================";
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
then find a name for your tests. The test directory starts with the test number::
|
||||||
|
|
||||||
|
<test_number><test_name>
|
||||||
|
|
||||||
|
then put the rougail yaml file in the :file:`<test_number><test_name>/dictionaries`
|
||||||
|
folder.
|
||||||
|
|
||||||
|
.. note:: you need to run the test one, the test will populate the tiramisu code
|
||||||
|
in the `tiramisu` folder, just check the code and if the generated code looks
|
||||||
|
like what you expected, you can keep it as an expected test result.
|
||||||
|
|
||||||
|
HTML documentation
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
To generate locally the documentation :
|
||||||
|
|
||||||
|
- install sphinx: `pip install sphinx`
|
||||||
|
- install the read the docs theme with `pip3 install sphinx_rtd_theme`
|
||||||
|
- install the `sphinx-lesson <https://coderefinery.github.io/sphinx-lesson/>`_
|
||||||
|
package with `pip3 install sphinx-lesson`
|
||||||
|
|
||||||
|
Or more simply type `pip install -r requirements.txt` in the `docs` project's folder.
|
||||||
|
|
||||||
|
To generate the doc, type `make html` in the `docs` folder.
|
||||||
|
|
||||||
|
The go to the :file:`docs/_build/html` folder and launch a micro-web server,
|
||||||
|
for example::
|
||||||
|
|
||||||
|
python -m"http.server"
|
||||||
|
|
||||||
|
Open your web browser and nagivate in the docs at the `http://localhost:8000` url.
|
||||||
|
|
|
@ -15,6 +15,23 @@ It is with this name that we will be able to interact with the family.
|
||||||
|
|
||||||
It's best to follow the :ref:`convention on variable names`.
|
It's best to follow the :ref:`convention on variable names`.
|
||||||
|
|
||||||
|
Shorthand declaration
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Shorthand declaration is a way to declare a family in a single line. But you can only define family name and description.
|
||||||
|
|
||||||
|
To create a family, just add a key with it's name and variables as values. Attention, do not declare any other attributs.
|
||||||
|
|
||||||
|
By default, the description of the variable is the family name.
|
||||||
|
If you add comment in same line of name, this comment is use as description:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
my_family: # This is a great family
|
||||||
|
variable:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -92,20 +109,13 @@ Parameters
|
||||||
Dynamically created family
|
Dynamically created family
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
To create a family dynamically, you must create a fictitious family linked to a variable.
|
To create a family dynamically, you must create a fictitious family linked to a calculation.
|
||||||
The family name and description will actually be the prefix of the new name / description.
|
The family name will actually be the prefix of the new name. Alternativly you can specify the suffix in the name, ie `my_{{ suffix }}_name`.
|
||||||
The suffix will come from the value of the bound variable.
|
The suffix will come from the calculation.
|
||||||
The name of the families and variables it contains will be preserved, however, the description will be the prefix of the real description.
|
|
||||||
|
|
||||||
Obviously if the content of the linked variable were to evolve, new dynamic families will appear or disappear.
|
Obviously if the result of calculation were to evolve, new dynamic families will appear or disappear.
|
||||||
|
|
||||||
To note that:
|
Leader or follower variable
|
||||||
|
|
||||||
- the variable linked to the family must be a multiple variable
|
|
||||||
- it is not possible to put a simple family into a dynamic family
|
|
||||||
- it is possible to put a leading family into a dynamic family
|
|
||||||
|
|
||||||
Leader or foller variable
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
A leader family has a typical attribute of “leadership”. The type is required.
|
A leader family has a typical attribute of “leadership”. The type is required.
|
||||||
|
@ -150,12 +160,12 @@ If a leader variable is hidden or disabled, the follower variables will be hidde
|
||||||
Examples
|
Examples
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Simple family
|
Simple family:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_family:
|
my_family:
|
||||||
type: family
|
type: family
|
||||||
description: This is a great family
|
description: This is a great family
|
||||||
|
@ -168,7 +178,7 @@ Dynamically created family
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -176,18 +186,48 @@ Dynamically created family
|
||||||
- val2
|
- val2
|
||||||
my_dyn_family_:
|
my_dyn_family_:
|
||||||
type: dynamic
|
type: dynamic
|
||||||
|
dynamic:
|
||||||
|
type: variable
|
||||||
variable: rougail.varname
|
variable: rougail.varname
|
||||||
description: 'Describe '
|
description: 'Describe'
|
||||||
my_dyn_var:
|
my_dyn_var:
|
||||||
type: string
|
type: string
|
||||||
description: 'Variable description for '
|
description: 'Variable description'
|
||||||
|
|
||||||
This will dynamically create two families:
|
This will dynamically create two families:
|
||||||
|
|
||||||
- "rougail.my_dyn_family_val1" with the description "Describe val1"
|
- "rougail.my_dyn_family_val1"
|
||||||
- "rougail.my_dyn_family_val2" with the description "Describe val2"
|
- "rougail.my_dyn_family_val2"
|
||||||
|
|
||||||
In the dynamic family "rougail.my_dyn_family_val1" we will find a variable "my_dyn_var" with the description "Variable description for val1".
|
In the dynamic family "rougail.my_dyn_family_val1" we will find a variable "my_dyn_var".
|
||||||
|
|
||||||
|
Here is a second example:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
varname:
|
||||||
|
multi: true
|
||||||
|
default:
|
||||||
|
- val1
|
||||||
|
- val2
|
||||||
|
my_dyn_{{ suffix }}_family:
|
||||||
|
type: dynamic
|
||||||
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: rougail.varname
|
||||||
|
description: 'Describe'
|
||||||
|
my_dyn_var:
|
||||||
|
type: string
|
||||||
|
description: 'Variable description'
|
||||||
|
|
||||||
|
This will dynamically create two families:
|
||||||
|
|
||||||
|
- "rougail.my_dyn_val1_family"
|
||||||
|
- "rougail.my_dyn_val2_family"
|
||||||
|
|
||||||
|
In the dynamic family "rougail.my_dyn_val1_family" we will find a variable "my_dyn_var".
|
||||||
|
|
||||||
Leader or follower variable
|
Leader or follower variable
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
@ -200,7 +240,7 @@ Here is an example of defining a leading variable and two following variables:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
family:
|
family:
|
||||||
type: leadership
|
type: leadership
|
||||||
leader:
|
leader:
|
||||||
|
@ -217,7 +257,6 @@ To add a new follower variable, in a new dictionary, simply define one or more n
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
family:
|
family:
|
||||||
follower3:
|
follower3:
|
||||||
|
|
||||||
|
|
147
docs/fill.rst
147
docs/fill.rst
|
@ -156,6 +156,87 @@ There are two types of parameter:
|
||||||
- Name of the information whose value we want to retrieve.
|
- Name of the information whose value we want to retrieve.
|
||||||
- doc
|
- doc
|
||||||
|
|
||||||
|
The variable path
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Normal family
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The default namespace is defined in RougailConfig["variable_namespace"] with the default value "rougail".
|
||||||
|
In addition, there are extras namespaces defined with in RougailConfig["extra_dictionaries"].
|
||||||
|
|
||||||
|
Inside those namespaces we can add families and variables.
|
||||||
|
|
||||||
|
Here is an hierarchic examples:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
rougail
|
||||||
|
├── variable1
|
||||||
|
├── family1
|
||||||
|
│ ├── variable2
|
||||||
|
│ └── variable3
|
||||||
|
└── family2
|
||||||
|
└── subfamily1
|
||||||
|
└── variable4
|
||||||
|
extra1
|
||||||
|
└── family3
|
||||||
|
├── variable5
|
||||||
|
└── variable6
|
||||||
|
|
||||||
|
In `calculation` we can use other variables.
|
||||||
|
|
||||||
|
Here is all paths:
|
||||||
|
|
||||||
|
- rougail.variable1
|
||||||
|
- rougail.family1.variable2
|
||||||
|
- rougail.family1.variable3
|
||||||
|
- rougail.family2.subfamily1.variable4
|
||||||
|
- extra1.family3.variable5
|
||||||
|
- extra1.family3.variable6
|
||||||
|
|
||||||
|
Inside a variable's `calculation` we can use relative path. "_" means that other variable is in same family. "__" means that other variables are in parent family, and so on...
|
||||||
|
|
||||||
|
For example, in variable2's `calculation`, we can use relative path:
|
||||||
|
|
||||||
|
- __.variable1
|
||||||
|
- _.variable3
|
||||||
|
- __.family2.subfamily1.variable4
|
||||||
|
|
||||||
|
But we cannot access to extra1 variables with relative path.
|
||||||
|
|
||||||
|
Dynamic family
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Hire is a dynamic family "{{ suffix }}":
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
rougail
|
||||||
|
├── variable1: ["val1", "val2"]
|
||||||
|
├── {{ suffix }}
|
||||||
|
│ ├── variable2
|
||||||
|
│ └── variable3
|
||||||
|
└── family
|
||||||
|
└── variable4
|
||||||
|
|
||||||
|
For variable2's calculation, we can use:
|
||||||
|
|
||||||
|
- rougail.{{ suffix }}.variable3
|
||||||
|
- _.variable3
|
||||||
|
|
||||||
|
In this case, we get value for "variable3" with the same suffix as "variable2".
|
||||||
|
|
||||||
|
For variable4's calculation, we have two possibility:
|
||||||
|
|
||||||
|
- retrieves all values with all suffixes:
|
||||||
|
|
||||||
|
- rougail.{{ suffix }}.variable3
|
||||||
|
- __.{{ suffix }}.variable3
|
||||||
|
|
||||||
|
- retrieves a value for a specified suffix:
|
||||||
|
|
||||||
|
- rougail.val1.variable3
|
||||||
|
- __.val1.variable3
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -167,7 +248,7 @@ Let's start with an example from a simple Jinja template:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
default:
|
default:
|
||||||
type: jinja
|
type: jinja
|
||||||
|
@ -178,7 +259,7 @@ Here is a second example with a boolean variable:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
type: boolean
|
type: boolean
|
||||||
default:
|
default:
|
||||||
|
@ -190,7 +271,7 @@ And a multiple value of the number type:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
type: number
|
type: number
|
||||||
multi: true
|
multi: true
|
||||||
|
@ -206,7 +287,7 @@ Let's create a variable whose value is returned by a python function:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
default:
|
default:
|
||||||
type: jinja
|
type: jinja
|
||||||
|
@ -224,7 +305,7 @@ An example with parameters:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
description: my description
|
description: my description
|
||||||
default:
|
default:
|
||||||
|
@ -235,19 +316,19 @@ An example with parameters:
|
||||||
param1: value
|
param1: value
|
||||||
param2:
|
param2:
|
||||||
type: variable
|
type: variable
|
||||||
variable: rougail.unknown_variable
|
variable: _.unknown_variable
|
||||||
optional: true
|
optional: true
|
||||||
param3:
|
param3:
|
||||||
type: information
|
type: information
|
||||||
information: doc
|
information: doc
|
||||||
variable: rougail.my_calculated_variable
|
variable: _.my_calculated_variable
|
||||||
|
|
||||||
An example with a `suffix` type parameter:
|
An example with a `suffix` type parameter:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -255,7 +336,9 @@ An example with a `suffix` type parameter:
|
||||||
- val2
|
- val2
|
||||||
my_dyn_family_:
|
my_dyn_family_:
|
||||||
type: dynamic
|
type: dynamic
|
||||||
variable: rougail.varname
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: _.varname
|
||||||
description: 'Describe '
|
description: 'Describe '
|
||||||
my_dyn_var:
|
my_dyn_var:
|
||||||
type: string
|
type: string
|
||||||
|
@ -275,7 +358,7 @@ An example with an index type parameter:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
family:
|
family:
|
||||||
type: leadership
|
type: leadership
|
||||||
leader:
|
leader:
|
||||||
|
@ -299,7 +382,7 @@ Copy a variable in another:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -309,14 +392,14 @@ Copy a variable in another:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
type: variable
|
type: variable
|
||||||
variable: rougail.my_variable
|
variable: _.my_variable
|
||||||
|
|
||||||
Copy one variable to another if the source has no `property` problem:
|
Copy one variable to another if the source has no `property` problem:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
default: val1
|
default: val1
|
||||||
disabled: true
|
disabled: true
|
||||||
|
@ -324,7 +407,7 @@ Copy one variable to another if the source has no `property` problem:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
type: variable
|
type: variable
|
||||||
variable: rougail.my_variable
|
variable: _.my_variable
|
||||||
propertyerror: false
|
propertyerror: false
|
||||||
|
|
||||||
Copy two non-multiple variables into a multiple variable:
|
Copy two non-multiple variables into a multiple variable:
|
||||||
|
@ -332,7 +415,7 @@ Copy two non-multiple variables into a multiple variable:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable_1:
|
my_variable_1:
|
||||||
default: val1
|
default: val1
|
||||||
my_variable_2:
|
my_variable_2:
|
||||||
|
@ -341,9 +424,9 @@ Copy two non-multiple variables into a multiple variable:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
- type: variable
|
- type: variable
|
||||||
variable: rougail.my_variable_1
|
variable: _.my_variable_1
|
||||||
- type: variable
|
- type: variable
|
||||||
variable: rougail.my_variable_2
|
variable: _.my_variable_2
|
||||||
|
|
||||||
A variable in a dynamic family can also be used in a calculation.
|
A variable in a dynamic family can also be used in a calculation.
|
||||||
|
|
||||||
|
@ -352,7 +435,7 @@ For example using the variable for a particular suffix:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -360,7 +443,9 @@ For example using the variable for a particular suffix:
|
||||||
- val2
|
- val2
|
||||||
my_dyn_family_:
|
my_dyn_family_:
|
||||||
type: dynamic
|
type: dynamic
|
||||||
variable: rougail.varname
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: _.varname
|
||||||
description: 'Describe '
|
description: 'Describe '
|
||||||
my_dyn_var_:
|
my_dyn_var_:
|
||||||
type: string
|
type: string
|
||||||
|
@ -369,7 +454,7 @@ For example using the variable for a particular suffix:
|
||||||
all_dyn_var:
|
all_dyn_var:
|
||||||
default:
|
default:
|
||||||
type: variable
|
type: variable
|
||||||
variable: rougail.my_dyn_family_val1.my_dyn_var_val1
|
variable: _.my_dyn_family_val1.my_dyn_var_val1
|
||||||
|
|
||||||
In this case, we recover the value `val1`.
|
In this case, we recover the value `val1`.
|
||||||
|
|
||||||
|
@ -378,7 +463,7 @@ Second example using the variable for all suffixes:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -386,7 +471,9 @@ Second example using the variable for all suffixes:
|
||||||
- val2
|
- val2
|
||||||
my_dyn_family_:
|
my_dyn_family_:
|
||||||
type: dynamic
|
type: dynamic
|
||||||
variable: rougail.varname
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: _.varname
|
||||||
description: 'Describe '
|
description: 'Describe '
|
||||||
my_dyn_var_:
|
my_dyn_var_:
|
||||||
type: string
|
type: string
|
||||||
|
@ -396,7 +483,7 @@ Second example using the variable for all suffixes:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
type: variable
|
type: variable
|
||||||
variable: rougail.my_dyn_family_.my_dyn_var_
|
variable: _.my_dyn_family_.my_dyn_var_
|
||||||
|
|
||||||
In this case, we recover the `val1` and `val2` list.
|
In this case, we recover the `val1` and `val2` list.
|
||||||
|
|
||||||
|
@ -406,7 +493,7 @@ Calculation via a suffix
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
varname:
|
varname:
|
||||||
multi: true
|
multi: true
|
||||||
default:
|
default:
|
||||||
|
@ -414,7 +501,9 @@ Calculation via a suffix
|
||||||
- val2
|
- val2
|
||||||
my_dyn_family_:
|
my_dyn_family_:
|
||||||
type: dynamic
|
type: dynamic
|
||||||
variable: rougail.varname
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: _.varname
|
||||||
description: 'Describe '
|
description: 'Describe '
|
||||||
my_dyn_var_:
|
my_dyn_var_:
|
||||||
type: string
|
type: string
|
||||||
|
@ -427,7 +516,7 @@ Calculation via an index
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
family:
|
family:
|
||||||
type: leadership
|
type: leadership
|
||||||
leader:
|
leader:
|
||||||
|
@ -448,7 +537,7 @@ In a first dictionary, let's declare our variable and our calculation:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
default:
|
default:
|
||||||
type: jinja
|
type: jinja
|
||||||
|
@ -459,7 +548,7 @@ In a second dictionary, it is possible to redefine the calculation:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
redefine: true
|
redefine: true
|
||||||
default:
|
default:
|
||||||
|
@ -471,7 +560,7 @@ In a third dictionary, we even can delete the calculation if needed:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_calculated_variable:
|
my_calculated_variable:
|
||||||
redefine: true
|
redefine: true
|
||||||
default: null
|
default: null
|
||||||
|
|
|
@ -15,7 +15,7 @@ What is a consistency handling system ?
|
||||||
|
|
||||||
Tiramisu
|
Tiramisu
|
||||||
|
|
||||||
|Tiramisu| is a consistency handling system that was initially designed
|
|Tiramisu| is a consistency handling system that has initially been designed
|
||||||
in the configuration management scope. To put it more simply,
|
in the configuration management scope. To put it more simply,
|
||||||
this library is generally used to handle configuration options.
|
this library is generally used to handle configuration options.
|
||||||
|
|
||||||
|
@ -44,16 +44,16 @@ The dictionaries
|
||||||
|
|
||||||
.. image:: images/schema.png
|
.. image:: images/schema.png
|
||||||
|
|
||||||
The main advantage is that declaring variables and writing consistency is a simple
|
The main advantage is that declaring variables and writing consistency is as simple
|
||||||
as writing YAML. It is not necessary to write :term:`Tiramisu` code.
|
as writing YAML. With Rougail it is not necessary to write :term:`Tiramisu` code any more.
|
||||||
It simplifies a lot of things.
|
It simplifies a lot of things.
|
||||||
|
|
||||||
And rather than writing :term:`Tiramisu` code, we can declare variables and describe the relationships between variables in a declarative mode.
|
And rather than writing :term:`Tiramisu` code, we can declare variables and describe the relationships between variables in a declarative mode (that is, in a YAML file).
|
||||||
|
|
||||||
Once the dictionaries are loaded by Rougail, we find all the power of the :term:`Tiramisu` configuration management tool.
|
Once the dictionaries are loaded by Rougail, we find all the power of the :term:`Tiramisu` configuration management tool.
|
||||||
|
|
||||||
The dictionaries YAML format
|
The YAML dictionaries format
|
||||||
---------------------------------
|
-----------------------------
|
||||||
|
|
||||||
Before getting started with Rougail we need to learn the specifics of the YAML dictionaries file format (as well as some templating concepts).
|
Before getting started with Rougail we need to learn the specifics of the YAML dictionaries file format (as well as some templating concepts).
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ Here is a :term:`dictionary` example:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
description: Configure Proxy Access to the Internet
|
description: Configure Proxy Access to the Internet
|
||||||
type: family
|
type: family
|
||||||
|
@ -78,14 +78,13 @@ The variables
|
||||||
variable
|
variable
|
||||||
|
|
||||||
Here is a second definition of a :term:`variable`: it is a declaration unit that represents a business domain metaphor,
|
Here is a second definition of a :term:`variable`: it is a declaration unit that represents a business domain metaphor,
|
||||||
|
the most common example is that a variable that represents a configuration option
|
||||||
the most common example is that a variable represents a configuration option
|
|
||||||
in a application, but a variable represents something more that a configuration option.
|
in a application, but a variable represents something more that a configuration option.
|
||||||
It provides a business domain specific representation unit.
|
It provides a business domain specific representation unit.
|
||||||
|
|
||||||
.. note:: dictionaries can just define a list of variables, but we will see that
|
.. note:: Dictionaries can just define a list of variables, but we will see that
|
||||||
we can specify a lot more. We can define variables **and** their relations,
|
we can specify a lot more. We can define variables **and** their relations,
|
||||||
and the consistency between them.
|
**and** the consistency between them.
|
||||||
|
|
||||||
In the next step, we will explain through a tutorial how to construct a list of variables.
|
In the next step, we will explain through a tutorial how to construct a list of variables.
|
||||||
|
|
||||||
|
@ -125,9 +124,10 @@ Here is the tree structure we want to have::
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
:caption: The `hello.yaml` file
|
:caption: The `hello.yaml` file
|
||||||
|
:name: helloworld
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
hello:
|
hello:
|
||||||
default: world
|
default: world
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ Let's continuing on our "Hello world" theme and add a :term:`family` container.
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
world:
|
world:
|
||||||
description: Hello world family container
|
description: Hello world family container
|
||||||
name:
|
name:
|
||||||
|
|
100
docs/library.rst
100
docs/library.rst
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
Rougail is a configuration management library that allows you to load variables in a simple and convenient way.
|
Rougail is a configuration management library that allows you to load variables in a simple and convenient way.
|
||||||
|
|
||||||
In the following examples, we will use a specific configuration of Rougail. You will find all the options to :doc:`customize the directories structure used <configuration>`.
|
In the following examples, we will use a specific configuration of Rougail.
|
||||||
|
You will find all the configuraiton options in :doc:`configuration`.
|
||||||
|
|
||||||
To load the configuration you must import the `RougailConfig` class and set the `dictionaries_dir` values:
|
To load the configuration you must import the `RougailConfig` class and set the `dictionaries_dir` values:
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ Here is a first :file:`dict/00-base.yml` dictionary:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable:
|
my_variable:
|
||||||
default: my_value
|
default: my_value
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ Then let's create an extra :term:`dictionary` :file:`extras/00-base.yml`:
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
:caption: the :file:`extras/00-base.yml` file content
|
:caption: the :file:`extras/00-base.yml` file content
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable_extra:
|
my_variable_extra:
|
||||||
default: my_value_extra
|
default: my_value_extra
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ We create the complementary :term:`dictionary` named :file:`dict/01-function.yml
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
my_variable_jinja:
|
my_variable_jinja:
|
||||||
type: "string"
|
type: "string"
|
||||||
default:
|
default:
|
||||||
|
@ -139,3 +140,94 @@ Let's execute `script.py`:
|
||||||
{'rougail.my_variable': 'my_value', 'rougail.my_variable_jinja': 'no', 'example.my_variable_extra': 'my_value_extra'}
|
{'rougail.my_variable': 'my_value', 'rougail.my_variable_jinja': 'no', 'example.my_variable_extra': 'my_value_extra'}
|
||||||
|
|
||||||
The value of the `my_variable_extra` variable is calculated, and it's value comes from the :func:`return_no` function.
|
The value of the `my_variable_extra` variable is calculated, and it's value comes from the :func:`return_no` function.
|
||||||
|
|
||||||
|
Create your own type
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
A variable has a type. This type enables the variable to define the values that are accepted by this variable.
|
||||||
|
|
||||||
|
There is a series of default types, but obviously not all cases are taken.
|
||||||
|
|
||||||
|
It's possible to create your own type.
|
||||||
|
|
||||||
|
Here an example to a lipogram option (in a string, we cannot use "e" character):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: the `lipogram.py` file content
|
||||||
|
|
||||||
|
from tiramisu import StrOption
|
||||||
|
class LipogramOption(StrOption):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_type = 'lipogram'
|
||||||
|
|
||||||
|
def validate(self,
|
||||||
|
value):
|
||||||
|
super().validate(value)
|
||||||
|
# verify that there is any 'e' in the sentense
|
||||||
|
if 'e' in value:
|
||||||
|
raise ValueError('Perec wrote a book without any "e", you could not do it in a simple sentence?')
|
||||||
|
|
||||||
|
To add the new lipogram type in Rougail:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
>>> from rougail import Rougail, RougailConfig
|
||||||
|
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||||
|
>>> RougailConfig['custom_types']['lipogram'] = LipogramOption
|
||||||
|
|
||||||
|
Now, we can use lipogram type.
|
||||||
|
Here is a :file:`dict/00-base.yml` dictionary:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
var:
|
||||||
|
type: lipogram
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
>>> rougail = Rougail()
|
||||||
|
>>> config = rougail.get_config()
|
||||||
|
>>> config.option('rougail.var').value.set('blah')
|
||||||
|
>>> config.option('rougail.var').value.set('I just want to add a quality string that has no bad characters')
|
||||||
|
[...]
|
||||||
|
tiramisu.error.ValueOptionError: "I just want to add a quality string that has no bad characters" is an invalid lipogram for "var", Perec wrote a book without any "e", you could not do it in a simple sentence?
|
||||||
|
|
||||||
|
Upgrade dictionnaries to upper version
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
All dictionnaries has a format version number.
|
||||||
|
When a new format version is proposed, it is possible to automatically convert the files to the new version.
|
||||||
|
|
||||||
|
We create a term:`dictionary` named :file:`dict/01-upgrade.yml` with version 1.0:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
my_variable:
|
||||||
|
multi: true
|
||||||
|
my_dyn_family:
|
||||||
|
type: "dynamic"
|
||||||
|
variable: my_variable
|
||||||
|
a_variable:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
>>> from rougail import RougailUpgrade, RougailConfig
|
||||||
|
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||||
|
>>> upgrade = RougailUpgrade()
|
||||||
|
>>> upgrade.load_dictionaries('dict_converted')
|
||||||
|
|
||||||
|
The term:`dictionary` named :file:`dict_converted/01-upgrade.yml` is in version 1.1:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
version: '1.1'
|
||||||
|
my_variable:
|
||||||
|
multi: true
|
||||||
|
my_dyn_family:
|
||||||
|
type: dynamic
|
||||||
|
a_variable: null
|
||||||
|
dynamic:
|
||||||
|
type: variable
|
||||||
|
variable: my_variable
|
||||||
|
propertyerror: false
|
||||||
|
|
|
@ -35,7 +35,7 @@ Then let's put our first dictionary file in this folder, named :file:`00-proxy.y
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
description: Proxy configuration in order to have access to the internet
|
description: Proxy configuration in order to have access to the internet
|
||||||
type: family
|
type: family
|
||||||
|
@ -69,7 +69,7 @@ Let's create a second :file:`dict/01-proxy_mode.yml` file.
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
proxy_mode:
|
proxy_mode:
|
||||||
description: Proxy mode
|
description: Proxy mode
|
||||||
|
@ -122,7 +122,7 @@ Let's create the :file:`dict/02-proxy_manual.yml` dictionary:
|
||||||
:caption: the the :file:`dict/02-proxy_manual.yml` file
|
:caption: the the :file:`dict/02-proxy_manual.yml` file
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
manual:
|
manual:
|
||||||
description: Manual proxy configuration
|
description: Manual proxy configuration
|
||||||
|
@ -170,7 +170,7 @@ Let's create the :file:`dict/03-proxy_manual_http_proxy.yml` dictionary:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
manual:
|
manual:
|
||||||
http_proxy:
|
http_proxy:
|
||||||
|
@ -195,7 +195,7 @@ We then want to offer the user the possibility of providing the same proxy for t
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
:caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file
|
:caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file
|
||||||
|
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
manual:
|
manual:
|
||||||
use_for_https:
|
use_for_https:
|
||||||
|
@ -216,7 +216,7 @@ Let's create the :file:`dict/05-proxy_manual_ssl_proxy.yml` file:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
manual:
|
manual:
|
||||||
ssl_proxy:
|
ssl_proxy:
|
||||||
|
@ -271,9 +271,12 @@ Let's look at what happens if we try to access the `rougail.proxy.manual` variab
|
||||||
We have an error (with the message defined in the Jinja template):
|
We have an error (with the message defined in the Jinja template):
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: shell
|
||||||
|
|
||||||
|
tiramisu.error.PropertiesOptionError: cannot access to
|
||||||
|
optiondescription "Manual proxy configuration" because
|
||||||
|
has property "disabled" (the mode proxy is not manual)
|
||||||
|
|
||||||
tiramisu.error.PropertiesOptionError: cannot access to optiondescription "Manual proxy configuration" because has property "disabled" (the mode proxy is not manual)
|
|
||||||
|
|
||||||
Let's configure the proxy in manual mode
|
Let's configure the proxy in manual mode
|
||||||
|
|
||||||
|
@ -291,7 +294,7 @@ We can see that the returned variables does have the desired values:
|
||||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||||
'rougail.proxy.manual.use_for_https': True}
|
'rougail.proxy.manual.use_for_https': True}
|
||||||
|
|
||||||
Let's set the `read_only` mode:
|
Let's set the `read_only` mode and have a look at the configuration again:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -307,7 +310,7 @@ Let's set the `read_only` mode:
|
||||||
In the `read_only` mode, we can see that the HTTPS configuration appears.
|
In the `read_only` mode, we can see that the HTTPS configuration appears.
|
||||||
|
|
||||||
.. note:: We can see that `rougail.proxy.manual.http_proxy` values have been copied
|
.. note:: We can see that `rougail.proxy.manual.http_proxy` values have been copied
|
||||||
in `rougail.proxy.manual.ssl_proxy` too...
|
in `rougail.proxy.manual.ssl_proxy` too.
|
||||||
|
|
||||||
Changing values programmatically
|
Changing values programmatically
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -357,7 +360,7 @@ Let's create the :file:`dict/06-proxy_manual_socks_proxy.yml` file:
|
||||||
:caption: the :file:`dict/06-proxy_manual_socks_proxy.yml` file
|
:caption: the :file:`dict/06-proxy_manual_socks_proxy.yml` file
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
manual:
|
manual:
|
||||||
socks_proxy:
|
socks_proxy:
|
||||||
|
@ -389,7 +392,7 @@ Let's create the :file:`dict/07-proxy_auto.yml` file:
|
||||||
:caption: the :file:`dict/07-proxy_auto.yml` file
|
:caption: the :file:`dict/07-proxy_auto.yml` file
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
auto:
|
auto:
|
||||||
type: web_address
|
type: web_address
|
||||||
|
@ -416,7 +419,7 @@ Let's create the :file:`dict/07-proxy_no_proxy.yml` file:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
no_proxy:
|
no_proxy:
|
||||||
description: Address for which proxy will be desactivated
|
description: Address for which proxy will be desactivated
|
||||||
|
@ -507,7 +510,7 @@ Nothing special when creating the authentication request. To do this, let's crea
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
prompt_authentication:
|
prompt_authentication:
|
||||||
description: Prompt for authentication if password is saved
|
description: Prompt for authentication if password is saved
|
||||||
|
@ -532,7 +535,7 @@ Let's create a `dict/09-proxy_proxy_dns_socks5.yml` file:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
proxy_dns_socks5:
|
proxy_dns_socks5:
|
||||||
description: Use proxy DNS when using SOCKS v5
|
description: Use proxy DNS when using SOCKS v5
|
||||||
|
@ -575,7 +578,7 @@ Let's create a `dict/10-proxy_dns_over_https.yml` file:
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
dns_over_https:
|
dns_over_https:
|
||||||
description: DNS over HTTPS
|
description: DNS over HTTPS
|
||||||
|
@ -648,7 +651,7 @@ Here is the complete content of the FoxyProxy type proxy configuration
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
_type: leadership
|
_type: leadership
|
||||||
title:
|
title:
|
||||||
|
@ -810,7 +813,7 @@ If you prefer this option, here is a second extra dictionary :file:`foxyproxy/01
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
---
|
---
|
||||||
version: '1.0'
|
version: '1.1'
|
||||||
proxy:
|
proxy:
|
||||||
username:
|
username:
|
||||||
redefine: true
|
redefine: true
|
||||||
|
|
|
@ -23,6 +23,28 @@ Variable's associated symbolic name.
|
||||||
|
|
||||||
It's best to follow the :ref:`convention on variable names`.
|
It's best to follow the :ref:`convention on variable names`.
|
||||||
|
|
||||||
|
Shorthand declaration
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Shorthand declaration is a way to declare a variable in a single line. But you can only define variable name, description, multi or default value.
|
||||||
|
|
||||||
|
To create a variable, just add a key with it's name and default value as value.
|
||||||
|
Be careful not to declare any other attributes.
|
||||||
|
|
||||||
|
To declare a multi variable just add a list as default value.
|
||||||
|
|
||||||
|
By default, the description of the variable is the variable name.
|
||||||
|
If you add a comment in the same line of the name, this comment will be used has a description.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
my_variable: 1 # This is a great integer variable
|
||||||
|
my_multi_variable: # This is a great multi string variable
|
||||||
|
- value1
|
||||||
|
- value2
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,17 @@ classifiers = [
|
||||||
|
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyyaml ~= 6.0.1",
|
"ruamel.yaml ~= 0.17.40",
|
||||||
"pydantic ~= 2.5.2",
|
"pydantic ~= 2.5.2",
|
||||||
"jinja2 ~= 3.1.2",
|
"jinja2 ~= 3.1.2",
|
||||||
|
"tiramisu ~= 4.1.0"
|
||||||
]
|
]
|
||||||
[project.optional-dependancies]
|
|
||||||
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"pylint ~= 3.0.3",
|
"pylint ~= 3.0.3",
|
||||||
|
"pytest ~= 8.2.2",
|
||||||
|
"lxml ~= 5.2.2"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.commitizen]
|
[tool.commitizen]
|
||||||
|
|
|
@ -28,10 +28,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 tiramisu import Config
|
from tiramisu import Config
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
from .convert import RougailConvert
|
from .convert import RougailConvert
|
||||||
from .config import RougailConfig
|
from .config import RougailConfig
|
||||||
from .update import RougailUpgrade
|
from .update import RougailUpgrade
|
||||||
|
from .object_model import CONVERT_OPTION
|
||||||
|
|
||||||
|
|
||||||
def tiramisu_display_name(kls) -> str:
|
def tiramisu_display_name(kls) -> str:
|
||||||
|
@ -46,7 +48,7 @@ class Rougail:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
rougailconfig = None,
|
rougailconfig=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if rougailconfig is None:
|
if rougailconfig is None:
|
||||||
rougailconfig = RougailConfig
|
rougailconfig = RougailConfig
|
||||||
|
@ -66,7 +68,8 @@ class Rougail:
|
||||||
if not self.config:
|
if not self.config:
|
||||||
tiram_obj = self.converted.save(self.rougailconfig["tiramisu_cache"])
|
tiram_obj = self.converted.save(self.rougailconfig["tiramisu_cache"])
|
||||||
optiondescription = {}
|
optiondescription = {}
|
||||||
exec(tiram_obj, None, optiondescription) # pylint: disable=W0122
|
custom_types = {custom.__name__: custom for custom in self.rougailconfig["custom_types"].values()}
|
||||||
|
exec(tiram_obj, custom_types, optiondescription) # pylint: disable=W0122
|
||||||
self.config = Config(
|
self.config = Config(
|
||||||
optiondescription["option_0"],
|
optiondescription["option_0"],
|
||||||
display_name=tiramisu_display_name,
|
display_name=tiramisu_display_name,
|
||||||
|
@ -75,4 +78,4 @@ class Rougail:
|
||||||
return self.config
|
return self.config
|
||||||
|
|
||||||
|
|
||||||
__ALL__ = ("Rougail", "RougailConvert", "RougailConfig", "RougailUpgrade")
|
__ALL__ = ("Rougail", "RougailConfig", "RougailUpgrade")
|
||||||
|
|
|
@ -27,27 +27,12 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
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 .variable import CONVERT_OPTION
|
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
from ..utils import load_modules
|
from ..utils import load_modules
|
||||||
|
|
||||||
|
|
||||||
ANNOTATORS = None
|
ANNOTATORS = None
|
||||||
#
|
|
||||||
#
|
|
||||||
# if not 'files' in dir(importlib.resources):
|
|
||||||
# # old python version
|
|
||||||
# class fake_files:
|
|
||||||
# def __init__(self, package):
|
|
||||||
# self.mod = []
|
|
||||||
# dir_package = dirname(importlib.resources._get_package(package).__file__)
|
|
||||||
# for mod in importlib.resources.contents(package):
|
|
||||||
# self.mod.append(join(dir_package, mod))
|
|
||||||
#
|
|
||||||
# def iterdir(self):
|
|
||||||
# return self.mod
|
|
||||||
# importlib.resources.files = fake_files
|
|
||||||
|
|
||||||
|
|
||||||
def get_level(module):
|
def get_level(module):
|
||||||
|
@ -101,4 +86,4 @@ class SpaceAnnotator: # pylint: disable=R0903
|
||||||
annotator(objectspace)
|
annotator(objectspace)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("SpaceAnnotator", "CONVERT_OPTION")
|
__all__ = ("SpaceAnnotator",)
|
||||||
|
|
|
@ -30,7 +30,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from rougail.i18n import _
|
from rougail.i18n import _
|
||||||
from rougail.error import DictConsistencyError
|
from rougail.error import DictConsistencyError
|
||||||
|
from rougail.utils import get_realpath
|
||||||
from rougail.annotator.variable import Walk
|
from rougail.annotator.variable import Walk
|
||||||
|
from rougail.object_model import VariableCalculation
|
||||||
|
|
||||||
|
|
||||||
class Mode: # pylint: disable=R0903
|
class Mode: # pylint: disable=R0903
|
||||||
|
@ -67,12 +69,23 @@ class Annotator(Walk):
|
||||||
name: Mode(idx)
|
name: Mode(idx)
|
||||||
for idx, name in enumerate(self.objectspace.rougailconfig["modes_level"])
|
for idx, name in enumerate(self.objectspace.rougailconfig["modes_level"])
|
||||||
}
|
}
|
||||||
|
self.check_leadership()
|
||||||
self.remove_empty_families()
|
self.remove_empty_families()
|
||||||
self.family_names()
|
self.family_names()
|
||||||
self.change_modes()
|
self.change_modes()
|
||||||
self.dynamic_families()
|
|
||||||
self.convert_help()
|
self.convert_help()
|
||||||
|
|
||||||
|
def check_leadership(self) -> None:
|
||||||
|
"""No subfamily in a leadership"""
|
||||||
|
for family in self.get_families():
|
||||||
|
if family.type != "leadership":
|
||||||
|
continue
|
||||||
|
for variable_path in self.objectspace.parents[family.path]:
|
||||||
|
variable = self.objectspace.paths[variable_path]
|
||||||
|
if variable.type in self.objectspace.family_types:
|
||||||
|
msg = f'the leadership "{family.path}" cannot have the { variable.type } "{ variable.path}"'
|
||||||
|
raise DictConsistencyError(msg, 24, variable.xmlfiles)
|
||||||
|
|
||||||
def remove_empty_families(self) -> None:
|
def remove_empty_families(self) -> None:
|
||||||
"""Remove all families without any variable"""
|
"""Remove all families without any variable"""
|
||||||
removed_families = []
|
removed_families = []
|
||||||
|
@ -104,9 +117,6 @@ class Annotator(Walk):
|
||||||
if not family.description:
|
if not family.description:
|
||||||
family.description = family.name
|
family.description = family.name
|
||||||
|
|
||||||
# family.doc = family.description
|
|
||||||
# del family.description
|
|
||||||
|
|
||||||
def change_modes(self):
|
def change_modes(self):
|
||||||
"""change the mode of variables"""
|
"""change the mode of variables"""
|
||||||
modes_level = self.objectspace.rougailconfig["modes_level"]
|
modes_level = self.objectspace.rougailconfig["modes_level"]
|
||||||
|
@ -162,6 +172,7 @@ class Annotator(Walk):
|
||||||
continue
|
continue
|
||||||
if leader is None and family.type == "leadership":
|
if leader is None and family.type == "leadership":
|
||||||
leader = variable
|
leader = variable
|
||||||
|
leader_mode = leader.mode
|
||||||
if variable_path in self.objectspace.families:
|
if variable_path in self.objectspace.families:
|
||||||
# set default mode a subfamily
|
# set default mode a subfamily
|
||||||
if family_mode and not self._has_mode(variable):
|
if family_mode and not self._has_mode(variable):
|
||||||
|
@ -172,9 +183,9 @@ class Annotator(Walk):
|
||||||
if leader:
|
if leader:
|
||||||
self._set_default_mode_leader(leader, variable)
|
self._set_default_mode_leader(leader, variable)
|
||||||
self._set_default_mode_variable(variable, family_mode)
|
self._set_default_mode_variable(variable, family_mode)
|
||||||
if leader:
|
if leader and leader_mode is not None:
|
||||||
# here because follower can change leader mode
|
# here because follower can change leader mode
|
||||||
self._set_auto_mode(family, leader.mode)
|
self._set_auto_mode(family, leader_mode)
|
||||||
|
|
||||||
def _has_mode(self, obj) -> bool:
|
def _has_mode(self, obj) -> bool:
|
||||||
return obj.mode and not obj.path in self.mode_auto
|
return obj.mode and not obj.path in self.mode_auto
|
||||||
|
@ -260,7 +271,7 @@ class Annotator(Walk):
|
||||||
# change variable mode, but not if variables are not in a family
|
# change variable mode, but not if variables are not in a family
|
||||||
is_leadership = family.type == "leadership"
|
is_leadership = family.type == "leadership"
|
||||||
if family.path in self.objectspace.parents:
|
if family.path in self.objectspace.parents:
|
||||||
for idx, variable_path in enumerate(self.objectspace.parents[family.path]):
|
for variable_path in self.objectspace.parents[family.path]:
|
||||||
variable = self.objectspace.paths[variable_path]
|
variable = self.objectspace.paths[variable_path]
|
||||||
if variable.type == "symlink":
|
if variable.type == "symlink":
|
||||||
continue
|
continue
|
||||||
|
@ -276,6 +287,10 @@ class Annotator(Walk):
|
||||||
if not family.mode:
|
if not family.mode:
|
||||||
# set the lower variable mode to family
|
# set the lower variable mode to family
|
||||||
self._set_auto_mode(family, min_variable_mode)
|
self._set_auto_mode(family, min_variable_mode)
|
||||||
|
if self.modes[family.mode] < self.modes[min_variable_mode]:
|
||||||
|
msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||||
|
f'families inside have the higher modes "{min_variable_mode}"')
|
||||||
|
raise DictConsistencyError(msg, 62, family.xmlfiles)
|
||||||
|
|
||||||
def _change_variable_mode(
|
def _change_variable_mode(
|
||||||
self,
|
self,
|
||||||
|
@ -304,18 +319,6 @@ class Annotator(Walk):
|
||||||
for family in self.get_families():
|
for family in self.get_families():
|
||||||
if family.type != "dynamic":
|
if family.type != "dynamic":
|
||||||
continue
|
continue
|
||||||
try:
|
|
||||||
family.variable = self.objectspace.paths[family.variable]
|
|
||||||
except AttributeError as err:
|
|
||||||
raise Exception(
|
|
||||||
f'cannot load the dynamic family "{family.path}", cannot find variable "{family.variable}"'
|
|
||||||
)
|
|
||||||
if not family.variable.multi:
|
|
||||||
msg = _(
|
|
||||||
f'dynamic family "{family.name}" must be linked '
|
|
||||||
f"to multi variable"
|
|
||||||
)
|
|
||||||
raise DictConsistencyError(msg, 16, family.xmlfiles)
|
|
||||||
for variable in self.objectspace.parents[family.path]:
|
for variable in self.objectspace.parents[family.path]:
|
||||||
if (
|
if (
|
||||||
isinstance(variable, self.objectspace.family)
|
isinstance(variable, self.objectspace.family)
|
||||||
|
|
|
@ -48,7 +48,7 @@ class Annotator(Walk): # pylint: disable=R0903
|
||||||
return
|
return
|
||||||
self.objectspace = objectspace
|
self.objectspace = objectspace
|
||||||
self.convert_value()
|
self.convert_value()
|
||||||
self.add_choice_nil()
|
self.valid_choices()
|
||||||
|
|
||||||
def convert_value(self) -> None:
|
def convert_value(self) -> None:
|
||||||
"""convert value"""
|
"""convert value"""
|
||||||
|
@ -66,58 +66,54 @@ class Annotator(Walk): # pylint: disable=R0903
|
||||||
if variable.type == "boolean" and multi is False and variable.default is None:
|
if variable.type == "boolean" and multi is False and variable.default is None:
|
||||||
variable.default = True
|
variable.default = True
|
||||||
|
|
||||||
if variable.default is None:
|
if variable.default is None or isinstance(variable.default, Calculation):
|
||||||
return
|
return
|
||||||
has_value = False
|
|
||||||
if isinstance(variable.default, Calculation):
|
if isinstance(variable.default, list):
|
||||||
pass
|
|
||||||
# variable.default = variable.default.to_function(self.functions)
|
|
||||||
elif isinstance(variable.default, list):
|
|
||||||
if not multi:
|
if not multi:
|
||||||
raise Exception(
|
msg = f'The variable "{variable.path}" with a list as default value must have "multi" attribute'
|
||||||
f'The variable "{variable.path}" with a list has default value must have "multi" attribute'
|
raise DictConsistencyError(msg, 68, variable.xmlfiles)
|
||||||
)
|
if variable.path in self.objectspace.followers and multi != "submulti":
|
||||||
if variable.path in self.objectspace.followers:
|
|
||||||
if multi != "submulti" and len(variable.default) != 1:
|
|
||||||
msg = _(
|
msg = _(
|
||||||
f'the follower "{variable.name}" without multi attribute can only have one value'
|
f'the follower "{variable.name}" without multi attribute can only have one value'
|
||||||
)
|
)
|
||||||
raise DictConsistencyError(msg, 87, variable.xmlfiles)
|
raise DictConsistencyError(msg, 87, variable.xmlfiles)
|
||||||
# else:
|
if not variable.default:
|
||||||
# variable.default = [value.name for value in variable.default]
|
variable.default = None
|
||||||
|
else:
|
||||||
if variable.path not in self.objectspace.leaders:
|
if variable.path not in self.objectspace.leaders:
|
||||||
if multi == "submulti":
|
if multi == "submulti":
|
||||||
self.objectspace.default_multi[
|
self.objectspace.default_multi[
|
||||||
variable.path
|
variable.path
|
||||||
] = variable.default # [value.name for value in variable.value]
|
] = variable.default
|
||||||
variable.default = None
|
variable.default = None
|
||||||
else:
|
else:
|
||||||
self.objectspace.default_multi[variable.path] = variable.default[
|
self.objectspace.default_multi[variable.path] = variable.default[
|
||||||
0
|
0
|
||||||
] # .name
|
]
|
||||||
has_value = True
|
|
||||||
elif variable.multi:
|
elif variable.multi:
|
||||||
# msg = _(f'the none multi variable "{variable.name}" cannot have '
|
msg = _(f'the variable "{variable.name}" is multi but has a non list default value')
|
||||||
# 'more than one value')
|
raise DictConsistencyError(msg, 12, variable.xmlfiles)
|
||||||
# raise DictConsistencyError(msg, 68, variable.xmlfiles)
|
elif variable.path in self.objectspace.followers:
|
||||||
raise Exception("pfff")
|
|
||||||
else:
|
|
||||||
if variable.path in self.objectspace.followers:
|
|
||||||
self.objectspace.default_multi[variable.path] = variable.default
|
self.objectspace.default_multi[variable.path] = variable.default
|
||||||
variable.default = None
|
variable.default = None
|
||||||
has_value = True
|
|
||||||
|
|
||||||
def add_choice_nil(self) -> None:
|
def valid_choices(self) -> None:
|
||||||
"""A variable with type "Choice" that is not mandatory must has "nil" value"""
|
"""A variable with type "Choice" that is not mandatory must has "nil" value"""
|
||||||
for variable in self.get_variables():
|
for variable in self.get_variables():
|
||||||
if variable.type != "choice":
|
if variable.type != "choice":
|
||||||
continue
|
continue
|
||||||
is_none = False
|
|
||||||
if isinstance(variable.choices, Calculation):
|
if isinstance(variable.choices, Calculation):
|
||||||
continue
|
continue
|
||||||
|
if variable.choices is None:
|
||||||
|
msg = f'the variable "{variable.path}" is a "choice" variable but don\'t have any choice'
|
||||||
|
raise DictConsistencyError(msg, 19, variable.xmlfiles)
|
||||||
|
if not variable.mandatory:
|
||||||
|
self.add_choice_nil(variable)
|
||||||
|
|
||||||
|
def add_choice_nil(self, variable) -> None:
|
||||||
|
"""A variable with type "Choice" that is not mandatory must has "nil" value"""
|
||||||
for choice in variable.choices:
|
for choice in variable.choices:
|
||||||
if choice is None:
|
if choice is None:
|
||||||
is_none = True
|
return
|
||||||
break
|
|
||||||
if not variable.mandatory and not is_none:
|
|
||||||
variable.choices.append(None)
|
variable.choices.append(None)
|
||||||
|
|
|
@ -31,58 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
from rougail.i18n import _
|
from rougail.i18n import _
|
||||||
from rougail.error import DictConsistencyError
|
from rougail.error import DictConsistencyError
|
||||||
from rougail.object_model import Calculation
|
from rougail.object_model import Calculation
|
||||||
|
from tiramisu.error import display_list
|
||||||
|
|
||||||
def convert_boolean(value: str) -> bool:
|
|
||||||
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
|
|
||||||
if isinstance(value, bool):
|
|
||||||
return value
|
|
||||||
value = value.lower()
|
|
||||||
if value == "true":
|
|
||||||
return True
|
|
||||||
elif value == "false":
|
|
||||||
return False
|
|
||||||
raise Exception(f"unknown boolean value {value}")
|
|
||||||
|
|
||||||
|
|
||||||
CONVERT_OPTION = {
|
|
||||||
"string": dict(opttype="StrOption"),
|
|
||||||
"number": dict(opttype="IntOption", func=int),
|
|
||||||
"float": dict(opttype="FloatOption", func=float),
|
|
||||||
"boolean": dict(opttype="BoolOption", func=convert_boolean),
|
|
||||||
"secret": dict(opttype="PasswordOption"),
|
|
||||||
"mail": dict(opttype="EmailOption"),
|
|
||||||
"unix_filename": dict(opttype="FilenameOption"),
|
|
||||||
"date": dict(opttype="DateOption"),
|
|
||||||
"unix_user": dict(opttype="UsernameOption"),
|
|
||||||
"ip": dict(opttype="IPOption", initkwargs={"allow_reserved": True}),
|
|
||||||
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}),
|
|
||||||
"netmask": dict(opttype="NetmaskOption"),
|
|
||||||
"network": dict(opttype="NetworkOption"),
|
|
||||||
"network_cidr": dict(opttype="NetworkOption", initkwargs={"cidr": True}),
|
|
||||||
"broadcast": dict(opttype="BroadcastOption"),
|
|
||||||
"netbios": dict(
|
|
||||||
opttype="DomainnameOption",
|
|
||||||
initkwargs={"type": "netbios", "warnings_only": True},
|
|
||||||
),
|
|
||||||
"domainname": dict(
|
|
||||||
opttype="DomainnameOption", initkwargs={"type": "domainname", "allow_ip": False}
|
|
||||||
),
|
|
||||||
"hostname": dict(
|
|
||||||
opttype="DomainnameOption", initkwargs={"type": "hostname", "allow_ip": False}
|
|
||||||
),
|
|
||||||
"web_address": dict(
|
|
||||||
opttype="URLOption", initkwargs={"allow_ip": False, "allow_without_dot": True}
|
|
||||||
),
|
|
||||||
"port": dict(opttype="PortOption", initkwargs={"allow_private": True}),
|
|
||||||
"mac": dict(opttype="MACOption"),
|
|
||||||
"unix_permissions": dict(
|
|
||||||
opttype="PermissionsOption", initkwargs={"warnings_only": True}, func=int
|
|
||||||
),
|
|
||||||
"choice": dict(opttype="ChoiceOption"),
|
|
||||||
#
|
|
||||||
"symlink": dict(opttype="SymLinkOption"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Walk:
|
class Walk:
|
||||||
|
@ -122,17 +71,48 @@ class Annotator(Walk): # pylint: disable=R0903
|
||||||
]
|
]
|
||||||
for extra in self.objectspace.rougailconfig["extra_dictionaries"]:
|
for extra in self.objectspace.rougailconfig["extra_dictionaries"]:
|
||||||
self.forbidden_name.append(extra)
|
self.forbidden_name.append(extra)
|
||||||
|
# default type inference from a default value with :term:`basic types`
|
||||||
|
self.basic_types = {str: "string", int: "number", bool: "boolean", float: "float"}
|
||||||
self.convert_variable()
|
self.convert_variable()
|
||||||
self.convert_test()
|
self.convert_test()
|
||||||
self.convert_help()
|
self.convert_help()
|
||||||
|
self.verify_choices()
|
||||||
|
|
||||||
def convert_variable(self):
|
def convert_variable(self):
|
||||||
"""convert variable"""
|
"""convert variable"""
|
||||||
for variable in self.get_variables():
|
for variable in self.get_variables():
|
||||||
if variable.type == "symlink":
|
if variable.type == "symlink":
|
||||||
continue
|
continue
|
||||||
|
if variable.version != "1.0":
|
||||||
|
self._convert_variable_inference(variable)
|
||||||
|
if variable.multi is None:
|
||||||
|
variable.multi = False
|
||||||
|
if variable.type is None:
|
||||||
|
variable.type = "string"
|
||||||
self._convert_variable(variable)
|
self._convert_variable(variable)
|
||||||
|
|
||||||
|
def _convert_variable_inference(
|
||||||
|
self,
|
||||||
|
variable,
|
||||||
|
) -> None:
|
||||||
|
# variable has no type
|
||||||
|
if variable.type is None:
|
||||||
|
# choice type inference from the `choices` attribute
|
||||||
|
if variable.choices is not None:
|
||||||
|
variable.type = "choice"
|
||||||
|
elif variable.default not in [None, []]:
|
||||||
|
if isinstance(variable.default, list):
|
||||||
|
tested_value = variable.default[0]
|
||||||
|
else:
|
||||||
|
tested_value = variable.default
|
||||||
|
variable.type = self.basic_types.get(type(tested_value), None)
|
||||||
|
# variable has no multi attribute
|
||||||
|
if variable.multi is None:
|
||||||
|
if variable.path in self.objectspace.leaders:
|
||||||
|
variable.multi = True
|
||||||
|
else:
|
||||||
|
variable.multi = isinstance(variable.default, list)
|
||||||
|
|
||||||
def _convert_variable(
|
def _convert_variable(
|
||||||
self,
|
self,
|
||||||
variable: dict,
|
variable: dict,
|
||||||
|
@ -157,6 +137,9 @@ class Annotator(Walk): # pylint: disable=R0903
|
||||||
elif family.hidden:
|
elif family.hidden:
|
||||||
variable.hidden = family.hidden
|
variable.hidden = family.hidden
|
||||||
variable.hidden = None
|
variable.hidden = None
|
||||||
|
if variable.choices is not None and variable.type != 'choice':
|
||||||
|
msg = _(f'the variable "{variable.path}" has choice attribut but has not the "choice" type')
|
||||||
|
raise DictConsistencyError(msg, 11, variable.xmlfiles)
|
||||||
|
|
||||||
def convert_test(self):
|
def convert_test(self):
|
||||||
"""Convert variable tests value"""
|
"""Convert variable tests value"""
|
||||||
|
@ -175,3 +158,28 @@ class Annotator(Walk): # pylint: disable=R0903
|
||||||
continue
|
continue
|
||||||
self.objectspace.informations.add(variable.path, "help", variable.help)
|
self.objectspace.informations.add(variable.path, "help", variable.help)
|
||||||
del variable.help
|
del variable.help
|
||||||
|
|
||||||
|
def verify_choices(self):
|
||||||
|
for variable in self.get_variables():
|
||||||
|
if variable.default is None or variable.type != 'choice':
|
||||||
|
continue
|
||||||
|
if not isinstance(variable.choices, list):
|
||||||
|
continue
|
||||||
|
choices = variable.choices
|
||||||
|
has_calculation = False
|
||||||
|
for choice in choices:
|
||||||
|
if isinstance(choice, Calculation):
|
||||||
|
has_calculation = True
|
||||||
|
break
|
||||||
|
if has_calculation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
default = variable.default
|
||||||
|
if not isinstance(default, list):
|
||||||
|
default = [default]
|
||||||
|
for value in default:
|
||||||
|
if isinstance(value, Calculation):
|
||||||
|
continue
|
||||||
|
if value not in choices:
|
||||||
|
msg = _(f'the variable "{variable.path}" has an unvalid default value "{value}" should be in {display_list(choices, separator="or", add_quote=True)}')
|
||||||
|
raise DictConsistencyError(msg, 26, variable.xmlfiles)
|
||||||
|
|
|
@ -28,14 +28,14 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
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 os.path import join, abspath, dirname
|
from os.path import abspath, dirname, join
|
||||||
|
|
||||||
|
|
||||||
ROUGAILROOT = "/srv/rougail"
|
ROUGAILROOT = "/srv/rougail"
|
||||||
DTDDIR = join(dirname(abspath(__file__)), "data")
|
DTDDIR = join(dirname(abspath(__file__)), "data")
|
||||||
|
|
||||||
|
|
||||||
RougailConfig = {
|
RougailConfig = {
|
||||||
|
"default_dictionary_format_version": None,
|
||||||
"dictionaries_dir": [join(ROUGAILROOT, "dictionaries")],
|
"dictionaries_dir": [join(ROUGAILROOT, "dictionaries")],
|
||||||
"extra_dictionaries": {},
|
"extra_dictionaries": {},
|
||||||
"services_dir": [join(ROUGAILROOT, "services")],
|
"services_dir": [join(ROUGAILROOT, "services")],
|
||||||
|
@ -43,8 +43,6 @@ RougailConfig = {
|
||||||
"templates_dir": join(ROUGAILROOT, "templates"),
|
"templates_dir": join(ROUGAILROOT, "templates"),
|
||||||
"destinations_dir": join(ROUGAILROOT, "destinations"),
|
"destinations_dir": join(ROUGAILROOT, "destinations"),
|
||||||
"tmp_dir": join(ROUGAILROOT, "tmp"),
|
"tmp_dir": join(ROUGAILROOT, "tmp"),
|
||||||
"dtdfilename": join(DTDDIR, "rougail.dtd"),
|
|
||||||
"yamlschema_filename": join(DTDDIR, "rougail.yml"),
|
|
||||||
"functions_file": join(ROUGAILROOT, "functions.py"),
|
"functions_file": join(ROUGAILROOT, "functions.py"),
|
||||||
"system_service_directory": "/usr/lib/systemd/system",
|
"system_service_directory": "/usr/lib/systemd/system",
|
||||||
"systemd_service_destination_directory": "/usr/local/lib",
|
"systemd_service_destination_directory": "/usr/local/lib",
|
||||||
|
@ -77,4 +75,5 @@ RougailConfig = {
|
||||||
"force_convert_dyn_option_description": False,
|
"force_convert_dyn_option_description": False,
|
||||||
"suffix": "",
|
"suffix": "",
|
||||||
"tiramisu_cache": None,
|
"tiramisu_cache": None,
|
||||||
|
"custom_types": {},
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -73,3 +73,9 @@ class DictConsistencyError(Exception):
|
||||||
|
|
||||||
class UpgradeError(Exception):
|
class UpgradeError(Exception):
|
||||||
"""Error during XML upgrade"""
|
"""Error during XML upgrade"""
|
||||||
|
|
||||||
|
## ---- generic exceptions ----
|
||||||
|
|
||||||
|
class NotFoundError(Exception):
|
||||||
|
"not found error"
|
||||||
|
pass
|
||||||
|
|
|
@ -21,15 +21,88 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator
|
from typing import Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator
|
||||||
from pydantic import BaseModel, StrictBool, StrictInt, StrictFloat, StrictStr
|
from pydantic import (
|
||||||
|
BaseModel,
|
||||||
|
StrictBool,
|
||||||
|
StrictInt,
|
||||||
|
StrictFloat,
|
||||||
|
StrictStr,
|
||||||
|
ConfigDict,
|
||||||
|
)
|
||||||
from .utils import get_jinja_variable_to_param, get_realpath
|
from .utils import get_jinja_variable_to_param, get_realpath
|
||||||
|
from .error import DictConsistencyError
|
||||||
|
|
||||||
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
||||||
|
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
|
||||||
|
|
||||||
|
|
||||||
|
def convert_boolean(value: str) -> bool:
|
||||||
|
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`"""
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
value = value.lower()
|
||||||
|
if value == "true":
|
||||||
|
return True
|
||||||
|
elif value == "false":
|
||||||
|
return False
|
||||||
|
elif value in ['', None]:
|
||||||
|
return None
|
||||||
|
raise Exception(f'unknown boolean value "{value}"')
|
||||||
|
|
||||||
|
|
||||||
|
CONVERT_OPTION = {
|
||||||
|
"string": dict(opttype="StrOption"),
|
||||||
|
"number": dict(opttype="IntOption", func=int),
|
||||||
|
"float": dict(opttype="FloatOption", func=float),
|
||||||
|
"boolean": dict(opttype="BoolOption", func=convert_boolean),
|
||||||
|
"secret": dict(opttype="PasswordOption"),
|
||||||
|
"mail": dict(opttype="EmailOption"),
|
||||||
|
"unix_filename": dict(opttype="FilenameOption"),
|
||||||
|
"date": dict(opttype="DateOption"),
|
||||||
|
"unix_user": dict(opttype="UsernameOption"),
|
||||||
|
"ip": dict(opttype="IPOption", initkwargs={"allow_reserved": True}),
|
||||||
|
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}),
|
||||||
|
"netmask": dict(opttype="NetmaskOption"),
|
||||||
|
"network": dict(opttype="NetworkOption"),
|
||||||
|
"network_cidr": dict(opttype="NetworkOption", initkwargs={"cidr": True}),
|
||||||
|
"broadcast": dict(opttype="BroadcastOption"),
|
||||||
|
"netbios": dict(
|
||||||
|
opttype="DomainnameOption",
|
||||||
|
initkwargs={"type": "netbios", "warnings_only": True},
|
||||||
|
),
|
||||||
|
"domainname": dict(
|
||||||
|
opttype="DomainnameOption", initkwargs={"type": "domainname", "allow_ip": False}
|
||||||
|
),
|
||||||
|
"hostname": dict(
|
||||||
|
opttype="DomainnameOption", initkwargs={"type": "hostname", "allow_ip": False}
|
||||||
|
),
|
||||||
|
"web_address": dict(
|
||||||
|
opttype="URLOption", initkwargs={"allow_ip": False, "allow_without_dot": True}
|
||||||
|
),
|
||||||
|
"port": dict(opttype="PortOption", initkwargs={"allow_private": True}),
|
||||||
|
"mac": dict(opttype="MACOption"),
|
||||||
|
"unix_permissions": dict(
|
||||||
|
opttype="PermissionsOption", initkwargs={"warnings_only": True}, func=int
|
||||||
|
),
|
||||||
|
"choice": dict(opttype="ChoiceOption"),
|
||||||
|
#
|
||||||
|
"symlink": dict(opttype="SymLinkOption"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Param(BaseModel):
|
class Param(BaseModel):
|
||||||
key: str
|
key: str
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
path,
|
||||||
|
attribute,
|
||||||
|
family_is_dynamic,
|
||||||
|
is_follower,
|
||||||
|
xmlfiles,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AnyParam(Param):
|
class AnyParam(Param):
|
||||||
|
@ -46,6 +119,15 @@ class VariableParam(Param):
|
||||||
|
|
||||||
class SuffixParam(Param):
|
class SuffixParam(Param):
|
||||||
type: str
|
type: str
|
||||||
|
suffix: Optional[int] = None
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
if not kwargs['family_is_dynamic']:
|
||||||
|
msg = f'suffix parameter for "{kwargs["attribute"]}" in "{kwargs["path"]}" cannot be set none dynamic family'
|
||||||
|
raise DictConsistencyError(msg, 10, kwargs['xmlfiles'])
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class InformationParam(Param):
|
class InformationParam(Param):
|
||||||
|
@ -57,6 +139,16 @@ class InformationParam(Param):
|
||||||
class IndexParam(Param):
|
class IndexParam(Param):
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
if not kwargs["is_follower"]:
|
||||||
|
msg = f'the variable "{kwargs["path"]}" is not a follower, so cannot have index type for param in "{kwargs["attribute"]}"'
|
||||||
|
raise DictConsistencyError(msg, 25, kwargs['xmlfiles'])
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PARAM_TYPES = {
|
PARAM_TYPES = {
|
||||||
"any": AnyParam,
|
"any": AnyParam,
|
||||||
|
@ -70,6 +162,12 @@ PARAM_TYPES = {
|
||||||
class Calculation(BaseModel):
|
class Calculation(BaseModel):
|
||||||
path_prefix: Optional[str]
|
path_prefix: Optional[str]
|
||||||
path: str
|
path: str
|
||||||
|
inside_list: bool
|
||||||
|
version: str
|
||||||
|
namespace: str
|
||||||
|
xmlfiles: List[str]
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
def get_realpath(
|
def get_realpath(
|
||||||
self,
|
self,
|
||||||
|
@ -84,24 +182,30 @@ class Calculation(BaseModel):
|
||||||
for param_obj in self.params:
|
for param_obj in self.params:
|
||||||
param = param_obj.model_dump()
|
param = param_obj.model_dump()
|
||||||
if param.get("type") == "variable":
|
if param.get("type") == "variable":
|
||||||
variable_path = self.get_realpath(param["variable"])
|
variable, suffix = objectspace.paths.get_with_dynamic(
|
||||||
variable, suffix, dynamic = objectspace.paths.get_with_dynamic(variable_path)
|
param["variable"], self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
|
||||||
|
)
|
||||||
if not variable:
|
if not variable:
|
||||||
if not param.get("optional"):
|
if not param.get("optional"):
|
||||||
raise Exception(f"cannot find {variable_path}")
|
raise Exception(f"cannot find {param['variable']}")
|
||||||
continue
|
continue
|
||||||
if not isinstance(variable, objectspace.variable):
|
if not isinstance(variable, objectspace.variable):
|
||||||
raise Exception("pfff it's a family")
|
raise Exception("pfff it's a family")
|
||||||
param["variable"] = variable
|
param["variable"] = variable
|
||||||
if suffix:
|
if suffix:
|
||||||
param["suffix"] = suffix
|
param["suffix"] = suffix
|
||||||
param["dynamic"] = dynamic
|
|
||||||
if param.get("type") == "information":
|
if param.get("type") == "information":
|
||||||
if param["variable"]:
|
if param["variable"]:
|
||||||
variable_path = self.get_realpath(param["variable"])
|
variable, suffix = objectspace.paths.get_with_dynamic(
|
||||||
param["variable"] = objectspace.paths[variable_path]
|
param["variable"], self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
|
||||||
if not param["variable"]:
|
)
|
||||||
raise Exception("pffff")
|
if not variable:
|
||||||
|
msg = f'cannot find variable "{param["variable"]}" defined in "{self.attribute_name}" for "{self.path}"'
|
||||||
|
raise DictConsistencyError(msg, 14, self.xmlfiles)
|
||||||
|
param["variable"] = variable
|
||||||
|
if suffix:
|
||||||
|
msg = f'variable "{param["variable"]}" defined in "{self.attribute_name}" for "{self.path}" is a dynamic variable'
|
||||||
|
raise DictConsistencyError(msg, 15, self.xmlfiles)
|
||||||
else:
|
else:
|
||||||
del param["variable"]
|
del param["variable"]
|
||||||
params[param.pop("key")] = param
|
params[param.pop("key")] = param
|
||||||
|
@ -110,12 +214,11 @@ class Calculation(BaseModel):
|
||||||
|
|
||||||
class JinjaCalculation(Calculation):
|
class JinjaCalculation(Calculation):
|
||||||
attribute_name: Literal[
|
attribute_name: Literal[
|
||||||
"frozen", "hidden", "mandatory", "disabled", "default", "validators", "choices"
|
"frozen", "hidden", "mandatory", "disabled", "default", "validators", "choices", "dynamic"
|
||||||
]
|
]
|
||||||
jinja: StrictStr
|
jinja: StrictStr
|
||||||
params: Optional[List[Param]] = None
|
params: Optional[List[Param]] = None
|
||||||
return_type: BASETYPE = None
|
return_type: BASETYPE = None
|
||||||
inside_list: bool
|
|
||||||
|
|
||||||
def _jinja_to_function(
|
def _jinja_to_function(
|
||||||
self,
|
self,
|
||||||
|
@ -148,21 +251,23 @@ class JinjaCalculation(Calculation):
|
||||||
default["params"] |= self.get_params(objectspace)
|
default["params"] |= self.get_params(objectspace)
|
||||||
if params:
|
if params:
|
||||||
default["params"] |= params
|
default["params"] |= params
|
||||||
for sub_variable, suffix, true_path, dynamic in get_jinja_variable_to_param(
|
for sub_variable, suffix, true_path in get_jinja_variable_to_param(
|
||||||
|
self.path,
|
||||||
self.jinja,
|
self.jinja,
|
||||||
objectspace,
|
objectspace,
|
||||||
variable.xmlfiles,
|
variable.xmlfiles,
|
||||||
objectspace.functions,
|
objectspace.functions,
|
||||||
self.path_prefix,
|
self.path_prefix,
|
||||||
|
self.version,
|
||||||
|
self.namespace,
|
||||||
):
|
):
|
||||||
if isinstance(sub_variable, objectspace.variable):
|
if sub_variable.path in objectspace.variables:
|
||||||
default["params"][true_path] = {
|
default["params"][true_path] = {
|
||||||
"type": "variable",
|
"type": "variable",
|
||||||
"variable": sub_variable,
|
"variable": sub_variable,
|
||||||
}
|
}
|
||||||
if suffix:
|
if suffix:
|
||||||
default["params"][true_path]["suffix"] = suffix
|
default["params"][true_path]["suffix"] = suffix
|
||||||
default["params"][true_path]["dynamic"] = dynamic
|
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def to_function(
|
def to_function(
|
||||||
|
@ -195,7 +300,7 @@ class JinjaCalculation(Calculation):
|
||||||
False,
|
False,
|
||||||
objectspace,
|
objectspace,
|
||||||
)
|
)
|
||||||
elif self.attribute_name in ["frozen", "hidden", "disabled", "mandatory"]:
|
elif self.attribute_name in PROPERTY_ATTRIBUTE:
|
||||||
if self.return_type:
|
if self.return_type:
|
||||||
raise Exception("return_type not allowed!")
|
raise Exception("return_type not allowed!")
|
||||||
return self._jinja_to_function(
|
return self._jinja_to_function(
|
||||||
|
@ -216,26 +321,36 @@ class JinjaCalculation(Calculation):
|
||||||
not self.inside_list,
|
not self.inside_list,
|
||||||
objectspace,
|
objectspace,
|
||||||
)
|
)
|
||||||
|
elif self.attribute_name == "dynamic":
|
||||||
|
return self._jinja_to_function(
|
||||||
|
"jinja_to_function",
|
||||||
|
"string",
|
||||||
|
True,
|
||||||
|
objectspace,
|
||||||
|
)
|
||||||
raise Exception("hu?")
|
raise Exception("hu?")
|
||||||
|
|
||||||
|
|
||||||
class VariableCalculation(Calculation):
|
class VariableCalculation(Calculation):
|
||||||
attribute_name: Literal[
|
attribute_name: Literal[
|
||||||
"frozen", "hidden", "mandatory", "disabled", "default", "choices"
|
"frozen", "hidden", "mandatory", "disabled", "default", "choices", "dynamic"
|
||||||
]
|
]
|
||||||
variable: StrictStr
|
variable: StrictStr
|
||||||
propertyerror: bool = True
|
propertyerror: bool = True
|
||||||
inside_list: bool
|
allow_none: bool = False
|
||||||
|
|
||||||
def to_function(
|
def to_function(
|
||||||
self,
|
self,
|
||||||
objectspace,
|
objectspace,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
variable_path = self.get_realpath(self.variable)
|
variable, suffix = objectspace.paths.get_with_dynamic(
|
||||||
variable, suffix, dynamic = objectspace.paths.get_with_dynamic(variable_path)
|
self.variable, self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
|
||||||
|
)
|
||||||
if not variable:
|
if not variable:
|
||||||
raise Exception(f"pffff {variable_path}")
|
msg = f'Variable not found "{self.variable}" for attribut "{self.attribute_name}" for variable "{self.path}"'
|
||||||
|
raise DictConsistencyError(msg, 88, self.xmlfiles)
|
||||||
if not isinstance(variable, objectspace.variable):
|
if not isinstance(variable, objectspace.variable):
|
||||||
|
# FIXME remove the pfff
|
||||||
raise Exception("pfff it's a family")
|
raise Exception("pfff it's a family")
|
||||||
param = {
|
param = {
|
||||||
"type": "variable",
|
"type": "variable",
|
||||||
|
@ -244,29 +359,39 @@ class VariableCalculation(Calculation):
|
||||||
}
|
}
|
||||||
if suffix:
|
if suffix:
|
||||||
param["suffix"] = suffix
|
param["suffix"] = suffix
|
||||||
param["dynamic"] = dynamic
|
|
||||||
params = {None: [param]}
|
params = {None: [param]}
|
||||||
function = "calc_value"
|
function = "calc_value"
|
||||||
help_function = None
|
help_function = None
|
||||||
if self.attribute_name in ["frozen", "hidden", "disabled", "mandatory"]:
|
if self.attribute_name in PROPERTY_ATTRIBUTE:
|
||||||
function = "variable_to_property"
|
function = "variable_to_property"
|
||||||
help_function = "variable_to_property"
|
help_function = "variable_to_property"
|
||||||
if variable.type != "boolean":
|
if variable.type != "boolean":
|
||||||
raise Exception("only boolean!")
|
raise Exception("only boolean!")
|
||||||
params[None].insert(0, self.attribute_name)
|
params[None].insert(0, self.attribute_name)
|
||||||
elif (
|
if self.allow_none:
|
||||||
self.attribute_name != "default" and variable.path not in objectspace.multis
|
|
||||||
):
|
|
||||||
raise Exception("pffff")
|
|
||||||
if not self.inside_list and self.path in objectspace.multis:
|
|
||||||
if (
|
|
||||||
not objectspace.paths.is_dynamic(variable_path)
|
|
||||||
and variable_path not in objectspace.multis
|
|
||||||
):
|
|
||||||
params["multi"] = True
|
|
||||||
params["allow_none"] = True
|
params["allow_none"] = True
|
||||||
if self.inside_list and variable.path in objectspace.multis:
|
# current variable is a multi
|
||||||
raise Exception("pfff")
|
if self.attribute_name in PROPERTY_ATTRIBUTE:
|
||||||
|
needs_multi = False
|
||||||
|
elif self.attribute_name != "default":
|
||||||
|
needs_multi = True
|
||||||
|
else:
|
||||||
|
needs_multi = self.path in objectspace.multis
|
||||||
|
calc_variable_is_multi = variable.path in objectspace.multis or (variable.path in objectspace.paths._dynamics and (suffix is None or suffix[-1] is None) and objectspace.paths._dynamics[variable.path] != objectspace.paths._dynamics.get(self.path))
|
||||||
|
if needs_multi:
|
||||||
|
if calc_variable_is_multi:
|
||||||
|
if self.inside_list:
|
||||||
|
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is multi but is inside a list'
|
||||||
|
raise DictConsistencyError(msg, 18, self.xmlfiles)
|
||||||
|
elif not self.inside_list:
|
||||||
|
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is not multi but is not inside a list'
|
||||||
|
raise DictConsistencyError(msg, 20, self.xmlfiles)
|
||||||
|
elif self.inside_list:
|
||||||
|
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", it\'s a list'
|
||||||
|
raise DictConsistencyError(msg, 23, self.xmlfiles)
|
||||||
|
elif calc_variable_is_multi:
|
||||||
|
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", the variable "{variable.path}" is a multi'
|
||||||
|
raise DictConsistencyError(msg, 21, self.xmlfiles)
|
||||||
ret = {
|
ret = {
|
||||||
"function": function,
|
"function": function,
|
||||||
"params": params,
|
"params": params,
|
||||||
|
@ -277,10 +402,9 @@ class VariableCalculation(Calculation):
|
||||||
|
|
||||||
|
|
||||||
class InformationCalculation(Calculation):
|
class InformationCalculation(Calculation):
|
||||||
attribute_name: Literal["default"]
|
attribute_name: Literal["default", "choice", "dynamic"]
|
||||||
information: StrictStr
|
information: StrictStr
|
||||||
variable: Optional[StrictStr]
|
variable: Optional[StrictStr]
|
||||||
inside_list: bool
|
|
||||||
|
|
||||||
def to_function(
|
def to_function(
|
||||||
self,
|
self,
|
||||||
|
@ -291,9 +415,10 @@ class InformationCalculation(Calculation):
|
||||||
"information": self.information,
|
"information": self.information,
|
||||||
}
|
}
|
||||||
if self.variable:
|
if self.variable:
|
||||||
variable_path = self.get_realpath(self.variable)
|
variable, suffix = objectspace.paths.get_with_dynamic(
|
||||||
variable = objectspace.paths[variable_path]
|
self.variable, self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
|
||||||
if variable is None:
|
)
|
||||||
|
if variable is None or suffix is not None:
|
||||||
raise Exception("pfff")
|
raise Exception("pfff")
|
||||||
param["variable"] = variable
|
param["variable"] = variable
|
||||||
return {
|
return {
|
||||||
|
@ -303,25 +428,32 @@ class InformationCalculation(Calculation):
|
||||||
|
|
||||||
|
|
||||||
class SuffixCalculation(Calculation):
|
class SuffixCalculation(Calculation):
|
||||||
attribute_name: Literal["default"]
|
attribute_name: Literal["default", "choice", "dynamic"]
|
||||||
|
suffix: Optional[int] = None
|
||||||
|
|
||||||
def to_function(
|
def to_function(
|
||||||
self,
|
self,
|
||||||
objectspace,
|
objectspace,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
suffix = {"type": "suffix"}
|
||||||
|
if self.suffix is not None:
|
||||||
|
suffix['suffix'] = self.suffix
|
||||||
return {
|
return {
|
||||||
"function": "calc_value",
|
"function": "calc_value",
|
||||||
"params": {None: [{"type": "suffix"}]},
|
"params": {None: [suffix]},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class IndexCalculation(Calculation):
|
class IndexCalculation(Calculation):
|
||||||
attribute_name: Literal["default"]
|
attribute_name: Literal["default", "choice", "dynamic"]
|
||||||
|
|
||||||
def to_function(
|
def to_function(
|
||||||
self,
|
self,
|
||||||
objectspace,
|
objectspace,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
if self.path not in objectspace.followers:
|
||||||
|
msg = f'the variable "{self.path}" is not a follower, so cannot have index type for "{self.attribute_name}"'
|
||||||
|
raise DictConsistencyError(msg, 60, self.xmlfiles)
|
||||||
return {
|
return {
|
||||||
"function": "calc_value",
|
"function": "calc_value",
|
||||||
"params": {None: [{"type": "index"}]},
|
"params": {None: [{"type": "index"}]},
|
||||||
|
@ -335,62 +467,40 @@ CALCULATION_TYPES = {
|
||||||
"suffix": SuffixCalculation,
|
"suffix": SuffixCalculation,
|
||||||
"index": IndexCalculation,
|
"index": IndexCalculation,
|
||||||
}
|
}
|
||||||
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None, Calculation]
|
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, Calculation, None]
|
||||||
|
|
||||||
|
|
||||||
class Family(BaseModel):
|
class Family(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
type: Literal["family", "leadership", "dynamic"] = "family"
|
type: Literal["family", "leadership", "dynamic"] = "family"
|
||||||
|
path: str
|
||||||
help: Optional[str] = None
|
help: Optional[str] = None
|
||||||
mode: Optional[str] = None
|
mode: Optional[str] = None
|
||||||
hidden: Union[bool, Calculation] = False
|
hidden: Union[bool, Calculation] = False
|
||||||
disabled: Union[bool, Calculation] = False
|
disabled: Union[bool, Calculation] = False
|
||||||
|
namespace: Optional[str]
|
||||||
xmlfiles: List[str] = []
|
xmlfiles: List[str] = []
|
||||||
path: str
|
|
||||||
|
|
||||||
class ConfigDict:
|
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
|
|
||||||
class Dynamic(Family):
|
class Dynamic(Family):
|
||||||
variable: str
|
variable: str=None
|
||||||
|
# None only for format 1.0
|
||||||
|
dynamic: Union[List[Union[StrictStr, Calculation]], Calculation]
|
||||||
|
|
||||||
|
|
||||||
class Variable(BaseModel):
|
class Variable(BaseModel):
|
||||||
|
# type will be set dynamically in `annotator/value.py`, default is None
|
||||||
|
type: str = None
|
||||||
name: str
|
name: str
|
||||||
type: Literal[
|
|
||||||
"number",
|
|
||||||
"float",
|
|
||||||
"string",
|
|
||||||
"password",
|
|
||||||
"secret",
|
|
||||||
"mail",
|
|
||||||
"boolean",
|
|
||||||
"unix_filename",
|
|
||||||
"date",
|
|
||||||
"unix_user",
|
|
||||||
"ip",
|
|
||||||
"local_ip",
|
|
||||||
"netmask",
|
|
||||||
"network",
|
|
||||||
"broadcast",
|
|
||||||
"netbios",
|
|
||||||
"domainname",
|
|
||||||
"hostname",
|
|
||||||
"web_address",
|
|
||||||
"port",
|
|
||||||
"mac",
|
|
||||||
"cidr",
|
|
||||||
"network_cidr",
|
|
||||||
"choice",
|
|
||||||
"unix_permissions",
|
|
||||||
] = "string"
|
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None
|
default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None
|
||||||
|
choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None
|
||||||
params: Optional[List[Param]] = None
|
params: Optional[List[Param]] = None
|
||||||
validators: Optional[List[Calculation]] = None
|
validators: Optional[List[Calculation]] = None
|
||||||
multi: bool = False
|
multi: Optional[bool] = None
|
||||||
unique: Optional[bool] = None
|
unique: Optional[bool] = None
|
||||||
help: Optional[str] = None
|
help: Optional[str] = None
|
||||||
hidden: Union[bool, Calculation] = False
|
hidden: Union[bool, Calculation] = False
|
||||||
|
@ -399,20 +509,24 @@ class Variable(BaseModel):
|
||||||
auto_save: bool = False
|
auto_save: bool = False
|
||||||
mode: Optional[str] = None
|
mode: Optional[str] = None
|
||||||
test: Optional[list] = None
|
test: Optional[list] = None
|
||||||
xmlfiles: List[str] = []
|
|
||||||
path: str
|
path: str
|
||||||
|
namespace: str
|
||||||
|
version: str
|
||||||
|
xmlfiles: List[str] = []
|
||||||
|
|
||||||
class ConfigDict:
|
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
|
|
||||||
class Choice(Variable):
|
#class Choice(Variable):
|
||||||
choices: Union[List[BASETYPE_CALC], Calculation]
|
# type: Literal["choice"] = "choice"
|
||||||
|
# choices: Union[List[BASETYPE_CALC], Calculation]
|
||||||
|
|
||||||
|
|
||||||
class SymLink(BaseModel):
|
class SymLink(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
type: str = "symlink"
|
type: Literal["symlink"] = "symlink"
|
||||||
opt: Variable
|
opt: Variable
|
||||||
xmlfiles: List[str] = []
|
xmlfiles: List[str] = []
|
||||||
path: str
|
path: str
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
|
@ -1,527 +0,0 @@
|
||||||
"""Manage path to find objects
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
Silique (https://www.silique.fr)
|
|
||||||
Copyright (C) 2022-2024
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from typing import List
|
|
||||||
from .i18n import _
|
|
||||||
from .error import DictConsistencyError
|
|
||||||
from .utils import normalize_family
|
|
||||||
|
|
||||||
|
|
||||||
class Path:
|
|
||||||
"""Helper class to handle the `path` attribute.
|
|
||||||
|
|
||||||
sample: path="creole.general.condition"
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
rougailconfig: "RougailConfig",
|
|
||||||
) -> None:
|
|
||||||
self.variables = {}
|
|
||||||
self.families = {}
|
|
||||||
# self.names = {}
|
|
||||||
self.full_paths_families = {}
|
|
||||||
self.full_paths_variables = {}
|
|
||||||
self.full_dyn_paths_families = {}
|
|
||||||
self.valid_enums = {}
|
|
||||||
self.variable_namespace = rougailconfig["variable_namespace"]
|
|
||||||
self.providers = {}
|
|
||||||
self.suppliers = {}
|
|
||||||
self.list_conditions = {}
|
|
||||||
self.suffix = rougailconfig["suffix"]
|
|
||||||
self.index = 0
|
|
||||||
|
|
||||||
def set_path_prefix(self, prefix: str) -> None:
|
|
||||||
self._path_prefix = prefix
|
|
||||||
if prefix:
|
|
||||||
if None in self.full_paths_families:
|
|
||||||
raise DictConsistencyError(
|
|
||||||
_(f'prefix "{prefix}" cannot be set if a prefix "None" exists'),
|
|
||||||
39,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for old_prefix in self.full_paths_families:
|
|
||||||
if old_prefix != None:
|
|
||||||
raise DictConsistencyError(
|
|
||||||
_(f"no prefix cannot be set if a prefix exists"), 84, None
|
|
||||||
)
|
|
||||||
if prefix in self.full_paths_families:
|
|
||||||
raise DictConsistencyError(_(f'prefix "{prefix}" already exists'), 83, None)
|
|
||||||
self.full_paths_families[prefix] = {}
|
|
||||||
self.full_paths_variables[prefix] = {}
|
|
||||||
self.valid_enums[prefix] = {}
|
|
||||||
self.providers[prefix] = {}
|
|
||||||
self.suppliers[prefix] = {}
|
|
||||||
self.list_conditions[prefix] = {}
|
|
||||||
|
|
||||||
def has_path_prefix(self) -> bool:
|
|
||||||
return None not in self.full_paths_families
|
|
||||||
|
|
||||||
def get_path_prefixes(self) -> list:
|
|
||||||
return list(self.full_paths_families)
|
|
||||||
|
|
||||||
def get_path_prefix(self) -> str:
|
|
||||||
return self._path_prefix
|
|
||||||
|
|
||||||
# Family
|
|
||||||
def add_family(
|
|
||||||
self,
|
|
||||||
namespace: str,
|
|
||||||
subpath: str,
|
|
||||||
variableobj: str,
|
|
||||||
is_dynamic: str,
|
|
||||||
force_path_prefix: str = None,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Add a new family"""
|
|
||||||
if force_path_prefix is None:
|
|
||||||
force_path_prefix = self._path_prefix
|
|
||||||
path = subpath + "." + variableobj.name
|
|
||||||
if namespace == self.variable_namespace:
|
|
||||||
if variableobj.name in self.full_paths_families[force_path_prefix]:
|
|
||||||
msg = _(f'Duplicate family name "{variableobj.name}"')
|
|
||||||
raise DictConsistencyError(msg, 55, variableobj.xmlfiles)
|
|
||||||
self.full_paths_families[force_path_prefix][variableobj.name] = path
|
|
||||||
if is_dynamic:
|
|
||||||
if subpath in self.full_dyn_paths_families:
|
|
||||||
dyn_subpath = self.full_dyn_paths_families[subpath]
|
|
||||||
else:
|
|
||||||
dyn_subpath = subpath
|
|
||||||
self.full_dyn_paths_families[
|
|
||||||
path
|
|
||||||
] = f"{dyn_subpath}.{variableobj.name}{{suffix}}"
|
|
||||||
if path in self.families:
|
|
||||||
msg = _(f'Duplicate family name "{path}"')
|
|
||||||
raise DictConsistencyError(msg, 37, variableobj.xmlfiles)
|
|
||||||
if path in self.variables:
|
|
||||||
msg = _(f'A variable and a family has the same path "{path}"')
|
|
||||||
raise DictConsistencyError(msg, 56, variableobj.xmlfiles)
|
|
||||||
self.families[path] = dict(
|
|
||||||
name=path,
|
|
||||||
namespace=namespace,
|
|
||||||
variableobj=variableobj,
|
|
||||||
)
|
|
||||||
self.set_name(variableobj, "optiondescription_")
|
|
||||||
variableobj.path = path
|
|
||||||
variableobj.path_prefix = force_path_prefix
|
|
||||||
|
|
||||||
def get_family(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
current_namespace: str,
|
|
||||||
path_prefix: str,
|
|
||||||
allow_variable_namespace: bool = False,
|
|
||||||
) -> "Family": # pylint: disable=C0111
|
|
||||||
"""Get a family"""
|
|
||||||
if (
|
|
||||||
current_namespace == self.variable_namespace or allow_variable_namespace
|
|
||||||
) and path in self.full_paths_families[path_prefix]:
|
|
||||||
path = self.full_paths_families[path_prefix][path]
|
|
||||||
elif allow_variable_namespace and path_prefix:
|
|
||||||
path = f"{path_prefix}.{path}"
|
|
||||||
if path not in self.families:
|
|
||||||
raise DictConsistencyError(_(f'unknown option "{path}"'), 42, [])
|
|
||||||
dico = self.families[path]
|
|
||||||
if current_namespace != dico["namespace"] and (
|
|
||||||
not allow_variable_namespace or current_namespace != self.variable_namespace
|
|
||||||
):
|
|
||||||
msg = _(
|
|
||||||
f'A family located in the "{dico["namespace"]}" namespace '
|
|
||||||
f'shall not be used in the "{current_namespace}" namespace'
|
|
||||||
)
|
|
||||||
raise DictConsistencyError(msg, 38, [])
|
|
||||||
return dico["variableobj"]
|
|
||||||
|
|
||||||
def _get_dyn_path(
|
|
||||||
self,
|
|
||||||
subpath: str,
|
|
||||||
name: bool,
|
|
||||||
) -> str:
|
|
||||||
if subpath in self.full_dyn_paths_families:
|
|
||||||
subpath = self.full_dyn_paths_families[subpath]
|
|
||||||
path = f"{subpath}.{name}{{suffix}}"
|
|
||||||
else:
|
|
||||||
path = f"{subpath}.{name}"
|
|
||||||
return path
|
|
||||||
|
|
||||||
def set_provider(
|
|
||||||
self,
|
|
||||||
variableobj,
|
|
||||||
name,
|
|
||||||
family,
|
|
||||||
):
|
|
||||||
if not hasattr(variableobj, "provider"):
|
|
||||||
return
|
|
||||||
p_name = "provider:" + variableobj.provider
|
|
||||||
if "." in name:
|
|
||||||
msg = f'provider "{p_name}" not allowed in extra'
|
|
||||||
raise DictConsistencyError(msg, 82, variableobj.xmlfiles)
|
|
||||||
if p_name in self.providers[variableobj.path_prefix]:
|
|
||||||
msg = f'provider "{p_name}" declare multiple time'
|
|
||||||
raise DictConsistencyError(msg, 79, variableobj.xmlfiles)
|
|
||||||
self.providers[variableobj.path_prefix][p_name] = {
|
|
||||||
"path": self._get_dyn_path(
|
|
||||||
family,
|
|
||||||
name,
|
|
||||||
),
|
|
||||||
"option": variableobj,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_provider(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
path_prefix: str = None,
|
|
||||||
) -> "self.objectspace.variable":
|
|
||||||
return self.providers[path_prefix][name]["option"]
|
|
||||||
|
|
||||||
def get_providers_path(self, path_prefix=None):
|
|
||||||
if path_prefix:
|
|
||||||
return {
|
|
||||||
name: option["path"].split(".", 1)[-1]
|
|
||||||
for name, option in self.providers[path_prefix].items()
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name: option["path"] for name, option in self.providers[path_prefix].items()
|
|
||||||
}
|
|
||||||
|
|
||||||
def set_supplier(
|
|
||||||
self,
|
|
||||||
variableobj,
|
|
||||||
name,
|
|
||||||
family,
|
|
||||||
):
|
|
||||||
if not hasattr(variableobj, "supplier"):
|
|
||||||
return
|
|
||||||
s_name = "supplier:" + variableobj.supplier
|
|
||||||
if "." in name:
|
|
||||||
msg = f'supplier "{s_name}" not allowed in extra'
|
|
||||||
raise DictConsistencyError(msg, 82, variableobj.xmlfiles)
|
|
||||||
if s_name in self.suppliers[variableobj.path_prefix]:
|
|
||||||
msg = f'supplier "{s_name}" declare multiple time'
|
|
||||||
raise DictConsistencyError(msg, 79, variableobj.xmlfiles)
|
|
||||||
self.suppliers[variableobj.path_prefix][s_name] = {
|
|
||||||
"path": self._get_dyn_path(family, name),
|
|
||||||
"option": variableobj,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_supplier(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
path_prefix: str = None,
|
|
||||||
) -> "self.objectspace.variable":
|
|
||||||
return self.suppliers[path_prefix][name]["option"]
|
|
||||||
|
|
||||||
def get_suppliers_path(self, path_prefix=None):
|
|
||||||
if path_prefix:
|
|
||||||
return {
|
|
||||||
name: option["path"].split(".", 1)[-1]
|
|
||||||
for name, option in self.suppliers[path_prefix].items()
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name: option["path"] for name, option in self.suppliers[path_prefix].items()
|
|
||||||
}
|
|
||||||
|
|
||||||
# Variable
|
|
||||||
def add_variable(
|
|
||||||
self, # pylint: disable=R0913
|
|
||||||
namespace: str,
|
|
||||||
subpath: str,
|
|
||||||
variableobj: "self.objectspace.variable",
|
|
||||||
is_dynamic: bool = False,
|
|
||||||
is_leader: bool = False,
|
|
||||||
force_path_prefix: str = None,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Add a new variable (with path)"""
|
|
||||||
if force_path_prefix is None:
|
|
||||||
force_path_prefix = self._path_prefix
|
|
||||||
path = subpath + "." + variableobj.name
|
|
||||||
if namespace == self.variable_namespace:
|
|
||||||
self.full_paths_variables[force_path_prefix][variableobj.name] = path
|
|
||||||
if path in self.families:
|
|
||||||
msg = _(f'A family and a variable has the same path "{path}"')
|
|
||||||
raise DictConsistencyError(msg, 57, variableobj.xmlfiles)
|
|
||||||
if is_leader:
|
|
||||||
leader = subpath
|
|
||||||
else:
|
|
||||||
leader = None
|
|
||||||
self.variables[path] = dict(
|
|
||||||
name=path,
|
|
||||||
family=subpath,
|
|
||||||
leader=leader,
|
|
||||||
is_dynamic=is_dynamic,
|
|
||||||
variableobj=variableobj,
|
|
||||||
)
|
|
||||||
variableobj.path = path
|
|
||||||
variableobj.path_prefix = force_path_prefix
|
|
||||||
self.set_name(variableobj, "option_")
|
|
||||||
|
|
||||||
def set_name(
|
|
||||||
self,
|
|
||||||
variableobj,
|
|
||||||
option_prefix,
|
|
||||||
):
|
|
||||||
self.index += 1
|
|
||||||
variableobj.reflector_name = f"{option_prefix}{self.index}{self.suffix}"
|
|
||||||
|
|
||||||
def get_variable(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
namespace: str,
|
|
||||||
xmlfiles: List[str] = [],
|
|
||||||
allow_variable_namespace: bool = False,
|
|
||||||
force_path_prefix: str = None,
|
|
||||||
add_path_prefix: bool = False,
|
|
||||||
) -> "Variable": # pylint: disable=C0111
|
|
||||||
"""Get variable object from a path"""
|
|
||||||
if force_path_prefix is None:
|
|
||||||
force_path_prefix = self._path_prefix
|
|
||||||
try:
|
|
||||||
variable, suffix = self._get_variable(
|
|
||||||
name,
|
|
||||||
namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
xmlfiles=xmlfiles,
|
|
||||||
path_prefix=force_path_prefix,
|
|
||||||
add_path_prefix=add_path_prefix,
|
|
||||||
)
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if (
|
|
||||||
not allow_variable_namespace
|
|
||||||
or err.errno != 42
|
|
||||||
or namespace == self.variable_namespace
|
|
||||||
):
|
|
||||||
raise err from err
|
|
||||||
variable, suffix = self._get_variable(
|
|
||||||
name,
|
|
||||||
self.variable_namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
xmlfiles=xmlfiles,
|
|
||||||
path_prefix=force_path_prefix,
|
|
||||||
)
|
|
||||||
if suffix:
|
|
||||||
raise DictConsistencyError(_(f"{name} is a dynamic variable"), 36, [])
|
|
||||||
return variable["variableobj"]
|
|
||||||
|
|
||||||
def get_variable_family_path(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
namespace: str,
|
|
||||||
xmlfiles: List[str] = False,
|
|
||||||
force_path_prefix: str = None,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Get the full path of a family"""
|
|
||||||
if force_path_prefix is None:
|
|
||||||
force_path_prefix = self._path_prefix
|
|
||||||
return self._get_variable(
|
|
||||||
name,
|
|
||||||
namespace,
|
|
||||||
xmlfiles=xmlfiles,
|
|
||||||
path_prefix=force_path_prefix,
|
|
||||||
)["family"]
|
|
||||||
|
|
||||||
def get_variable_with_suffix(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
current_namespace: str,
|
|
||||||
xmlfiles: List[str],
|
|
||||||
path_prefix: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""get full path of a variable"""
|
|
||||||
try:
|
|
||||||
dico, suffix = self._get_variable(
|
|
||||||
name,
|
|
||||||
current_namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
xmlfiles=xmlfiles,
|
|
||||||
path_prefix=path_prefix,
|
|
||||||
add_path_prefix=True,
|
|
||||||
)
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if err.errno != 42 or current_namespace == self.variable_namespace:
|
|
||||||
raise err from err
|
|
||||||
dico, suffix = self._get_variable(
|
|
||||||
name,
|
|
||||||
self.variable_namespace,
|
|
||||||
with_suffix=True,
|
|
||||||
xmlfiles=xmlfiles,
|
|
||||||
path_prefix=path_prefix,
|
|
||||||
add_path_prefix=True,
|
|
||||||
)
|
|
||||||
namespace = dico["variableobj"].namespace
|
|
||||||
if (
|
|
||||||
namespace not in [self.variable_namespace, "services"]
|
|
||||||
and current_namespace != "services"
|
|
||||||
and current_namespace != namespace
|
|
||||||
):
|
|
||||||
msg = _(
|
|
||||||
f'A variable located in the "{namespace}" namespace shall not be used '
|
|
||||||
f'in the "{current_namespace}" namespace'
|
|
||||||
)
|
|
||||||
raise DictConsistencyError(msg, 41, xmlfiles)
|
|
||||||
return dico["variableobj"], suffix
|
|
||||||
|
|
||||||
def path_is_defined(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
namespace: str,
|
|
||||||
force_path_prefix: str = None,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""The path is a valid path"""
|
|
||||||
if namespace == self.variable_namespace:
|
|
||||||
if force_path_prefix is None:
|
|
||||||
force_path_prefix = self._path_prefix
|
|
||||||
return path in self.full_paths_variables[force_path_prefix]
|
|
||||||
return path in self.variables
|
|
||||||
|
|
||||||
def get_path(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
namespace: str,
|
|
||||||
) -> str:
|
|
||||||
if namespace == self.variable_namespace:
|
|
||||||
if path not in self.full_paths_variables[self._path_prefix]:
|
|
||||||
return None
|
|
||||||
path = self.full_paths_variables[self._path_prefix][path]
|
|
||||||
else:
|
|
||||||
path = f"{self._path_prefix}.{path}"
|
|
||||||
return path
|
|
||||||
|
|
||||||
def is_dynamic(self, variableobj) -> bool:
|
|
||||||
"""This variable is in dynamic family"""
|
|
||||||
return self._get_variable(
|
|
||||||
variableobj.path,
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)["is_dynamic"]
|
|
||||||
|
|
||||||
def is_leader(self, variableobj): # pylint: disable=C0111
|
|
||||||
"""Is the variable is a leader"""
|
|
||||||
path = variableobj.path
|
|
||||||
variable = self._get_variable(
|
|
||||||
path,
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
if not variable["leader"]:
|
|
||||||
return False
|
|
||||||
leadership = self.get_family(
|
|
||||||
variable["leader"],
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
return next(iter(leadership.variable.values())).path == path
|
|
||||||
|
|
||||||
def is_follower(self, variableobj) -> bool:
|
|
||||||
"""Is the variable is a follower"""
|
|
||||||
variable = self._get_variable(
|
|
||||||
variableobj.path,
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
if not variable["leader"]:
|
|
||||||
return False
|
|
||||||
leadership = self.get_family(
|
|
||||||
variable["leader"],
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
return next(iter(leadership.variable.values())).path != variableobj.path
|
|
||||||
|
|
||||||
def get_leader(self, variableobj) -> str:
|
|
||||||
variable = self._get_variable(
|
|
||||||
variableobj.path,
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
if not variable["leader"]:
|
|
||||||
raise Exception(f"cannot find leader for {variableobj.path}")
|
|
||||||
leadership = self.get_family(
|
|
||||||
variable["leader"],
|
|
||||||
variableobj.namespace,
|
|
||||||
path_prefix=variableobj.path_prefix,
|
|
||||||
)
|
|
||||||
return next(iter(leadership.variable.values()))
|
|
||||||
|
|
||||||
def _get_variable(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
namespace: str,
|
|
||||||
with_suffix: bool = False,
|
|
||||||
xmlfiles: List[str] = [],
|
|
||||||
path_prefix: str = None,
|
|
||||||
add_path_prefix: bool = False,
|
|
||||||
) -> str:
|
|
||||||
if namespace == self.variable_namespace:
|
|
||||||
if path in self.full_paths_variables[path_prefix]:
|
|
||||||
path = self.full_paths_variables[path_prefix][path]
|
|
||||||
else:
|
|
||||||
if with_suffix:
|
|
||||||
for var_name, full_path in self.full_paths_variables[
|
|
||||||
path_prefix
|
|
||||||
].items():
|
|
||||||
if not path.startswith(var_name):
|
|
||||||
continue
|
|
||||||
variable = self._get_variable(
|
|
||||||
full_path, namespace, path_prefix=path_prefix
|
|
||||||
)
|
|
||||||
if not variable["is_dynamic"]:
|
|
||||||
continue
|
|
||||||
return variable, path[len(var_name) :]
|
|
||||||
if path_prefix and add_path_prefix:
|
|
||||||
path = f"{path_prefix}.{path}"
|
|
||||||
elif path_prefix and add_path_prefix:
|
|
||||||
path = f"{path_prefix}.{path}"
|
|
||||||
# FIXME with_suffix and variable in extra?
|
|
||||||
if path not in self.variables:
|
|
||||||
raise DictConsistencyError(_(f'unknown option "{path}"'), 42, xmlfiles)
|
|
||||||
if with_suffix:
|
|
||||||
return self.variables[path], None
|
|
||||||
return self.variables[path]
|
|
||||||
|
|
||||||
def set_valid_enums(
|
|
||||||
self,
|
|
||||||
path,
|
|
||||||
values,
|
|
||||||
path_prefix,
|
|
||||||
):
|
|
||||||
self.valid_enums[path_prefix][path] = values
|
|
||||||
|
|
||||||
def has_valid_enums(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
path_prefix: str,
|
|
||||||
) -> bool:
|
|
||||||
return path in self.valid_enums[path_prefix]
|
|
||||||
|
|
||||||
def get_valid_enums(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
path_prefix: str,
|
|
||||||
):
|
|
||||||
return self.valid_enums[path_prefix][path]
|
|
|
@ -1,220 +0,0 @@
|
||||||
"""load XML and YAML file from directory
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
Silique (https://www.silique.fr)
|
|
||||||
Copyright (C) 2022-2024
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from typing import List
|
|
||||||
from os.path import join, isfile
|
|
||||||
from os import listdir
|
|
||||||
|
|
||||||
from lxml.etree import DTD, parse, XMLSyntaxError # pylint: disable=E0611
|
|
||||||
from pykwalify.compat import yml
|
|
||||||
from pykwalify.core import Core
|
|
||||||
from pykwalify.errors import SchemaError
|
|
||||||
|
|
||||||
|
|
||||||
from .i18n import _
|
|
||||||
from .error import DictConsistencyError
|
|
||||||
|
|
||||||
|
|
||||||
FORCE_SUBYAML = ["override"]
|
|
||||||
SCHEMA_DATA = {}
|
|
||||||
|
|
||||||
|
|
||||||
class Reflector:
|
|
||||||
"""Helper class for loading the Creole XML file,
|
|
||||||
parsing it, validating against the Creole DTD
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
rougailconfig: "RougailConfig",
|
|
||||||
) -> None:
|
|
||||||
"""Loads the Creole DTD
|
|
||||||
|
|
||||||
:raises IOError: if the DTD is not found
|
|
||||||
|
|
||||||
:param dtdfilename: the full filename of the Creole DTD
|
|
||||||
"""
|
|
||||||
dtdfilename = rougailconfig["dtdfilename"]
|
|
||||||
yamlschema_filename = rougailconfig["yamlschema_filename"]
|
|
||||||
if not isfile(dtdfilename):
|
|
||||||
raise IOError(_(f"no such DTD file: {dtdfilename}"))
|
|
||||||
with open(dtdfilename, "r") as dtdfd:
|
|
||||||
self.dtd = DTD(dtdfd)
|
|
||||||
if not isfile(yamlschema_filename):
|
|
||||||
raise IOError(_(f"no such YAML Schema file: {yamlschema_filename}"))
|
|
||||||
self.yamlschema_filename = yamlschema_filename
|
|
||||||
self.schema_data = None
|
|
||||||
|
|
||||||
def load_dictionaries_from_folders(
|
|
||||||
self,
|
|
||||||
folders: List[str],
|
|
||||||
just_doc: bool,
|
|
||||||
):
|
|
||||||
"""Loads all the dictionary files located in the folders' list
|
|
||||||
|
|
||||||
:param folders: list of full folder's name
|
|
||||||
"""
|
|
||||||
filenames = {}
|
|
||||||
for folder in folders:
|
|
||||||
for filename in listdir(folder):
|
|
||||||
if filename.endswith(".xml"):
|
|
||||||
ext = "xml"
|
|
||||||
full_filename = join(folder, filename)
|
|
||||||
elif filename.endswith(".yml"):
|
|
||||||
ext = "yml"
|
|
||||||
full_filename = join(folder, filename)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if filename in filenames:
|
|
||||||
raise DictConsistencyError(
|
|
||||||
_(f"duplicate dictionary file name {filename}"),
|
|
||||||
78,
|
|
||||||
[filenames[filename][1], full_filename],
|
|
||||||
)
|
|
||||||
filenames[filename] = (ext, full_filename)
|
|
||||||
if not filenames and not just_doc:
|
|
||||||
raise DictConsistencyError(_("there is no dictionary file"), 77, folders)
|
|
||||||
file_names = list(filenames.keys())
|
|
||||||
file_names.sort()
|
|
||||||
for filename in file_names:
|
|
||||||
ext, filename = filenames[filename]
|
|
||||||
if ext == "xml":
|
|
||||||
yield self.load_xml_file(filename)
|
|
||||||
else:
|
|
||||||
yield self.load_yml_file(filename)
|
|
||||||
|
|
||||||
def load_xml_file(
|
|
||||||
self,
|
|
||||||
filename: str,
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
document = parse(filename)
|
|
||||||
except XMLSyntaxError as err:
|
|
||||||
raise DictConsistencyError(
|
|
||||||
_(f"not a XML file: {err}"), 52, [filename]
|
|
||||||
) from err
|
|
||||||
if not self.dtd.validate(document):
|
|
||||||
dtd_error = self.dtd.error_log.filter_from_errors()[0]
|
|
||||||
msg = _(f"not a valid XML file: {dtd_error}")
|
|
||||||
raise DictConsistencyError(msg, 43, [filename])
|
|
||||||
return filename, document.getroot()
|
|
||||||
|
|
||||||
def load_yml_file(
|
|
||||||
self,
|
|
||||||
filename: str,
|
|
||||||
):
|
|
||||||
global SCHEMA_DATA
|
|
||||||
if self.yamlschema_filename not in SCHEMA_DATA:
|
|
||||||
with open(self.yamlschema_filename, "r") as fh:
|
|
||||||
SCHEMA_DATA[self.yamlschema_filename] = yml.load(fh)
|
|
||||||
try:
|
|
||||||
document = Core(
|
|
||||||
source_file=filename,
|
|
||||||
schema_data=SCHEMA_DATA[self.yamlschema_filename],
|
|
||||||
)
|
|
||||||
except XMLSyntaxError as err:
|
|
||||||
raise DictConsistencyError(
|
|
||||||
_(f"not a XML file: {err}"), 52, [filename]
|
|
||||||
) from err
|
|
||||||
try:
|
|
||||||
return filename, YParser(document.validate(raise_exception=True))
|
|
||||||
except SchemaError as yaml_error:
|
|
||||||
msg = _(f"not a valid YAML file: {yaml_error}")
|
|
||||||
raise DictConsistencyError(msg, 43, [filename])
|
|
||||||
|
|
||||||
|
|
||||||
class SubYAML:
|
|
||||||
def __init__(self, key, value):
|
|
||||||
if value is None:
|
|
||||||
value = {}
|
|
||||||
self.tag = key
|
|
||||||
self.dico = value
|
|
||||||
if "text" in value:
|
|
||||||
self.text = value["text"]
|
|
||||||
else:
|
|
||||||
self.text = None
|
|
||||||
if isinstance(value, list):
|
|
||||||
self.attrib = {}
|
|
||||||
else:
|
|
||||||
self.attrib = {
|
|
||||||
k: v
|
|
||||||
for k, v in value.items()
|
|
||||||
if not isinstance(v, list) and k not in FORCE_SUBYAML
|
|
||||||
}
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"<SubYAML {self.tag} at {id(self)}>"
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
if isinstance(self.dico, list):
|
|
||||||
lists = []
|
|
||||||
for dico in self.dico:
|
|
||||||
for key, value in dico.items():
|
|
||||||
if not isinstance(value, list):
|
|
||||||
value = [value]
|
|
||||||
lists.append((key, value))
|
|
||||||
else:
|
|
||||||
lists = []
|
|
||||||
for key, values in self.dico.items():
|
|
||||||
if key == "variables":
|
|
||||||
for v in values:
|
|
||||||
if "variable" in v:
|
|
||||||
lists.append(("variable", v["variable"]))
|
|
||||||
if "family" in v:
|
|
||||||
lists.append(("family", v["family"]))
|
|
||||||
else:
|
|
||||||
lists.append((key, values))
|
|
||||||
for key, values in lists:
|
|
||||||
if key not in FORCE_SUBYAML and not isinstance(values, list):
|
|
||||||
continue
|
|
||||||
if values is None:
|
|
||||||
values = [None]
|
|
||||||
for value in values:
|
|
||||||
yield SubYAML(key, value)
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
length = 0
|
|
||||||
for _ in self.__iter__():
|
|
||||||
length += 1
|
|
||||||
return length
|
|
||||||
|
|
||||||
|
|
||||||
class YParser:
|
|
||||||
def __init__(self, dico):
|
|
||||||
self.dico = dico
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
for key, values in self.dico.items():
|
|
||||||
if not isinstance(values, list):
|
|
||||||
continue
|
|
||||||
if key == "variables":
|
|
||||||
yield SubYAML(key, values)
|
|
||||||
else:
|
|
||||||
for val in values:
|
|
||||||
yield SubYAML(key, val)
|
|
|
@ -28,12 +28,89 @@ 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
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from tiramisu4 import DynOptionDescription
|
from tiramisu5 import DynOptionDescription, calc_value
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
from tiramisu import DynOptionDescription
|
from tiramisu import DynOptionDescription, calc_value
|
||||||
|
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||||
|
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||||
|
from jinja2 import StrictUndefined, DictLoader
|
||||||
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
|
from rougail import CONVERT_OPTION
|
||||||
|
from tiramisu.error import ValueWarning
|
||||||
from .utils import normalize_family
|
from .utils import normalize_family
|
||||||
|
|
||||||
|
|
||||||
|
global func
|
||||||
|
func = {'calc_value': calc_value}
|
||||||
|
dict_env = {}
|
||||||
|
ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined)
|
||||||
|
ENV.filters = func
|
||||||
|
ENV.compile_templates('jinja_caches', zip=None)
|
||||||
|
|
||||||
|
|
||||||
|
def load_functions(path):
|
||||||
|
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||||
|
loader = _SourceFileLoader('func', path)
|
||||||
|
spec = _spec_from_loader(loader.name, loader)
|
||||||
|
func_ = _module_from_spec(spec)
|
||||||
|
loader.exec_module(func_)
|
||||||
|
for function in dir(func_):
|
||||||
|
if function.startswith('_'):
|
||||||
|
continue
|
||||||
|
func[function] = getattr(func_, function)
|
||||||
|
|
||||||
|
|
||||||
|
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
|
||||||
|
global ENV, CONVERT_OPTION
|
||||||
|
kw = {}
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if '.' in key:
|
||||||
|
c_kw = kw
|
||||||
|
path, var = key.rsplit('.', 1)
|
||||||
|
for subkey in path.split('.'):
|
||||||
|
c_kw = c_kw.setdefault(subkey, {})
|
||||||
|
c_kw[var] = value
|
||||||
|
else:
|
||||||
|
kw[key] = value
|
||||||
|
values = ENV.get_template(__internal_jinja).render(kw, **func).strip()
|
||||||
|
convert = CONVERT_OPTION[__internal_type].get('func', str)
|
||||||
|
if __internal_multi:
|
||||||
|
return [convert(val) for val in values.split()]
|
||||||
|
values = convert(values)
|
||||||
|
return values if values != '' and values != 'None' else None
|
||||||
|
|
||||||
|
|
||||||
|
def variable_to_property(prop, value):
|
||||||
|
return prop if value else None
|
||||||
|
|
||||||
|
|
||||||
|
def jinja_to_property(prop, **kwargs):
|
||||||
|
value = func['jinja_to_function'](**kwargs)
|
||||||
|
return func['variable_to_property'](prop, value is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def jinja_to_property_help(prop, **kwargs):
|
||||||
|
value = func['jinja_to_function'](**kwargs)
|
||||||
|
return (prop, f'\"{prop}\" ({value})')
|
||||||
|
|
||||||
|
|
||||||
|
def valid_with_jinja(warnings_only=False, **kwargs):
|
||||||
|
global ValueWarning
|
||||||
|
value = func['jinja_to_function'](**kwargs)
|
||||||
|
if value:
|
||||||
|
if warnings_only:
|
||||||
|
raise ValueWarning(value)
|
||||||
|
else:
|
||||||
|
raise ValueError(value)
|
||||||
|
|
||||||
|
|
||||||
|
func['jinja_to_function'] = jinja_to_function
|
||||||
|
func['jinja_to_property'] = jinja_to_property
|
||||||
|
func['jinja_to_property_help'] = jinja_to_property_help
|
||||||
|
func['variable_to_property'] = variable_to_property
|
||||||
|
func['valid_with_jinja'] = valid_with_jinja
|
||||||
|
|
||||||
|
|
||||||
class ConvertDynOptionDescription(DynOptionDescription):
|
class ConvertDynOptionDescription(DynOptionDescription):
|
||||||
"""Suffix could be an integer, we should convert it in str
|
"""Suffix could be an integer, we should convert it in str
|
||||||
Suffix could also contain invalid character, so we should "normalize" it
|
Suffix could also contain invalid character, so we should "normalize" it
|
||||||
|
|
|
@ -28,15 +28,14 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
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 Optional
|
from typing import Optional, Union
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from os.path import isfile, basename
|
from os.path import isfile, basename
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .annotator import CONVERT_OPTION
|
|
||||||
from .error import DictConsistencyError
|
from .error import DictConsistencyError
|
||||||
from .utils import normalize_family
|
from .utils import normalize_family
|
||||||
from .object_model import Calculation
|
from .object_model import Calculation, CONVERT_OPTION
|
||||||
|
|
||||||
|
|
||||||
class BaseElt: # pylint: disable=R0903
|
class BaseElt: # pylint: disable=R0903
|
||||||
|
@ -60,8 +59,8 @@ class TiramisuReflector:
|
||||||
objectspace,
|
objectspace,
|
||||||
funcs_paths,
|
funcs_paths,
|
||||||
):
|
):
|
||||||
|
self.informations_idx = -1
|
||||||
self.rougailconfig = objectspace.rougailconfig
|
self.rougailconfig = objectspace.rougailconfig
|
||||||
self.jinja_added = False
|
|
||||||
self.reflector_objects = {}
|
self.reflector_objects = {}
|
||||||
self.text = {
|
self.text = {
|
||||||
"header": [],
|
"header": [],
|
||||||
|
@ -77,103 +76,28 @@ class TiramisuReflector:
|
||||||
"from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
|
"from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
for mode in self.rougailconfig["modes_level"]:
|
|
||||||
self.text["header"].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
|
|
||||||
if funcs_paths:
|
if funcs_paths:
|
||||||
if self.rougailconfig["export_with_import"]:
|
if self.rougailconfig["export_with_import"]:
|
||||||
self.text["header"].extend(
|
self.text["header"].extend(
|
||||||
[
|
["from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription"]
|
||||||
"from importlib.machinery import SourceFileLoader as _SourceFileLoader",
|
|
||||||
"from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec",
|
|
||||||
"global func",
|
|
||||||
"func = {'calc_value': calc_value}",
|
|
||||||
"",
|
|
||||||
"def _load_functions(path):",
|
|
||||||
" global _SourceFileLoader, _spec_from_loader, _module_from_spec, func",
|
|
||||||
" loader = _SourceFileLoader('func', path)",
|
|
||||||
" spec = _spec_from_loader(loader.name, loader)",
|
|
||||||
" func_ = _module_from_spec(spec)",
|
|
||||||
" loader.exec_module(func_)",
|
|
||||||
" for function in dir(func_):",
|
|
||||||
" if function.startswith('_'):",
|
|
||||||
" continue",
|
|
||||||
" func[function] = getattr(func_, function)",
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
for funcs_path in sorted(funcs_paths, key=sorted_func_name):
|
for funcs_path in sorted(funcs_paths, key=sorted_func_name):
|
||||||
if not isfile(funcs_path):
|
if not isfile(funcs_path):
|
||||||
continue
|
continue
|
||||||
self.text["header"].append(f"_load_functions('{funcs_path}')")
|
self.text["header"].append(f"load_functions('{funcs_path}')")
|
||||||
|
if self.rougailconfig["export_with_import"]:
|
||||||
|
for mode in self.rougailconfig["modes_level"]:
|
||||||
|
self.text["header"].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
|
||||||
self.objectspace = objectspace
|
self.objectspace = objectspace
|
||||||
self.make_tiramisu_objects()
|
self.make_tiramisu_objects()
|
||||||
if self.rougailconfig["export_with_import"] and (
|
|
||||||
self.rougailconfig["force_convert_dyn_option_description"]
|
|
||||||
or self.objectspace.has_dyn_option is True
|
|
||||||
):
|
|
||||||
self.text["header"].append(
|
|
||||||
"from rougail.tiramisu import ConvertDynOptionDescription"
|
|
||||||
)
|
|
||||||
for key, value in self.objectspace.jinja.items():
|
for key, value in self.objectspace.jinja.items():
|
||||||
self.add_jinja_to_function(key, value)
|
self.add_jinja_to_function(key, value)
|
||||||
|
|
||||||
def add_jinja_support(self):
|
|
||||||
if not self.jinja_added:
|
|
||||||
self.text["header"].extend(
|
|
||||||
[
|
|
||||||
"from jinja2 import StrictUndefined, DictLoader",
|
|
||||||
"from jinja2.sandbox import SandboxedEnvironment",
|
|
||||||
"from rougail.annotator.variable import CONVERT_OPTION",
|
|
||||||
"from tiramisu.error import ValueWarning",
|
|
||||||
"def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):",
|
|
||||||
" global ENV, CONVERT_OPTION",
|
|
||||||
" kw = {}",
|
|
||||||
" for key, value in kwargs.items():",
|
|
||||||
" if '.' in key:",
|
|
||||||
" c_kw = kw",
|
|
||||||
" path, var = key.rsplit('.', 1)",
|
|
||||||
" for subkey in path.split('.'):",
|
|
||||||
" c_kw = c_kw.setdefault(subkey, {})",
|
|
||||||
" c_kw[var] = value",
|
|
||||||
" else:",
|
|
||||||
" kw[key] = value",
|
|
||||||
" values = ENV.get_template(__internal_jinja).render(kw, **func).strip()",
|
|
||||||
" convert = CONVERT_OPTION[__internal_type].get('func', str)",
|
|
||||||
" if __internal_multi:",
|
|
||||||
" return [convert(val) for val in values.split()]",
|
|
||||||
" values = convert(values)",
|
|
||||||
" return values if values != '' and values != 'None' else None",
|
|
||||||
"def variable_to_property(prop, value):",
|
|
||||||
" return prop if value else None",
|
|
||||||
"def jinja_to_property(prop, **kwargs):",
|
|
||||||
" value = func['jinja_to_function'](**kwargs)",
|
|
||||||
" return func['variable_to_property'](prop, value is not None)",
|
|
||||||
"def jinja_to_property_help(prop, **kwargs):",
|
|
||||||
" value = func['jinja_to_function'](**kwargs)",
|
|
||||||
" return (prop, f'\"{prop}\" ({value})')",
|
|
||||||
"def valid_with_jinja(warnings_only=False, **kwargs):",
|
|
||||||
" global ValueWarning",
|
|
||||||
" value = func['jinja_to_function'](**kwargs)",
|
|
||||||
" if value:",
|
|
||||||
" if warnings_only:",
|
|
||||||
" raise ValueWarning(value)",
|
|
||||||
" else:",
|
|
||||||
" raise ValueError(value)",
|
|
||||||
"func['jinja_to_function'] = jinja_to_function",
|
|
||||||
"func['jinja_to_property'] = jinja_to_property",
|
|
||||||
"func['jinja_to_property_help'] = jinja_to_property_help",
|
|
||||||
"func['variable_to_property'] = variable_to_property",
|
|
||||||
"func['valid_with_jinja'] = valid_with_jinja",
|
|
||||||
"dict_env = {}",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
self.jinja_added = True
|
|
||||||
|
|
||||||
def add_jinja_to_function(
|
def add_jinja_to_function(
|
||||||
self,
|
self,
|
||||||
variable_name: str,
|
variable_name: str,
|
||||||
jinja: str,
|
jinja: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.add_jinja_support()
|
|
||||||
jinja_text = dumps(jinja, ensure_ascii=False)
|
jinja_text = dumps(jinja, ensure_ascii=False)
|
||||||
self.text["header"].append(f"dict_env['{variable_name}'] = {jinja_text}")
|
self.text["header"].append(f"dict_env['{variable_name}'] = {jinja_text}")
|
||||||
|
|
||||||
|
@ -243,16 +167,12 @@ class TiramisuReflector:
|
||||||
self.objectspace.set_name(elt, "optiondescription_")
|
self.objectspace.set_name(elt, "optiondescription_")
|
||||||
return self.objectspace.reflector_names[elt.path]
|
return self.objectspace.reflector_names[elt.path]
|
||||||
|
|
||||||
|
def get_information_name(self):
|
||||||
|
self.informations_idx += 1
|
||||||
|
return f"information_{self.informations_idx}"
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
"""Get text"""
|
"""Get text"""
|
||||||
if self.jinja_added:
|
|
||||||
self.text["header"].extend(
|
|
||||||
[
|
|
||||||
"ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined)",
|
|
||||||
"ENV.filters = func",
|
|
||||||
"ENV.compile_templates('jinja_caches', zip=None)",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
return "\n".join(self.text["header"] + self.text["option"])
|
return "\n".join(self.text["header"] + self.text["option"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,19 +190,22 @@ class Common:
|
||||||
self.tiramisu = tiramisu
|
self.tiramisu = tiramisu
|
||||||
tiramisu.reflector_objects[elt.path] = self
|
tiramisu.reflector_objects[elt.path] = self
|
||||||
self.object_type = None
|
self.object_type = None
|
||||||
|
self.informations = []
|
||||||
|
|
||||||
def get(self, calls, parent_name):
|
def get(self, calls, parent_name):
|
||||||
"""Get tiramisu's object"""
|
"""Get tiramisu's object"""
|
||||||
self_calls = calls.copy()
|
if self.elt.path in calls:
|
||||||
if self.elt.path in self_calls:
|
|
||||||
msg = f'"{self.elt.path}" will make an infinite loop'
|
msg = f'"{self.elt.path}" will make an infinite loop'
|
||||||
raise DictConsistencyError(msg, 80, self.elt.xmlfiles)
|
raise DictConsistencyError(msg, 80, self.elt.xmlfiles)
|
||||||
|
self_calls = calls.copy()
|
||||||
self_calls.append(self.elt.path)
|
self_calls.append(self.elt.path)
|
||||||
self.calls = self_calls
|
self.calls = self_calls
|
||||||
if self.option_name is None:
|
if self.option_name is None:
|
||||||
self.option_name = self.objectspace.reflector_names[self.elt.path]
|
self.option_name = self.objectspace.reflector_names[self.elt.path]
|
||||||
self.populate_attrib()
|
self.populate_attrib()
|
||||||
self.populate_informations()
|
if self.informations:
|
||||||
|
for information in self.informations:
|
||||||
|
self.tiramisu.text['option'].append(f'{information}.set_option({self.option_name})')
|
||||||
return self.option_name
|
return self.option_name
|
||||||
|
|
||||||
def populate_attrib(self):
|
def populate_attrib(self):
|
||||||
|
@ -295,6 +218,7 @@ class Common:
|
||||||
keys["properties"] = self.properties_to_string(
|
keys["properties"] = self.properties_to_string(
|
||||||
self.objectspace.properties[self.elt.path]
|
self.objectspace.properties[self.elt.path]
|
||||||
)
|
)
|
||||||
|
self.populate_informations(keys)
|
||||||
attrib = ", ".join([f"{key}={value}" for key, value in keys.items()])
|
attrib = ", ".join([f"{key}={value}" for key, value in keys.items()])
|
||||||
self.tiramisu.text["option"].append(
|
self.tiramisu.text["option"].append(
|
||||||
f"{self.option_name} = {self.object_type}({attrib})"
|
f"{self.option_name} = {self.object_type}({attrib})"
|
||||||
|
@ -323,8 +247,7 @@ class Common:
|
||||||
for property_, value in values.items():
|
for property_, value in values.items():
|
||||||
if value is True:
|
if value is True:
|
||||||
properties.append(self.convert_str(property_))
|
properties.append(self.convert_str(property_))
|
||||||
else:
|
elif isinstance(value, list):
|
||||||
if isinstance(value, list):
|
|
||||||
for val in value:
|
for val in value:
|
||||||
calc_properties.append(self.calculation_value(val))
|
calc_properties.append(self.calculation_value(val))
|
||||||
else:
|
else:
|
||||||
|
@ -351,17 +274,12 @@ class Common:
|
||||||
f"kwargs={{{kwargs}}}), func['calc_value_property_help'])"
|
f"kwargs={{{kwargs}}}), func['calc_value_property_help'])"
|
||||||
)
|
)
|
||||||
|
|
||||||
def populate_informations(self):
|
def populate_informations(self, keys):
|
||||||
"""Populate Tiramisu's informations"""
|
"""Populate Tiramisu's informations"""
|
||||||
informations = self.objectspace.informations.get(self.elt.path)
|
informations = self.objectspace.informations.get(self.elt.path)
|
||||||
if not informations:
|
if not informations:
|
||||||
return
|
return
|
||||||
for key, value in informations.items():
|
keys['informations'] = informations
|
||||||
if isinstance(value, str):
|
|
||||||
value = self.convert_str(value)
|
|
||||||
self.tiramisu.text["option"].append(
|
|
||||||
f"{self.option_name}.impl_set_information('{key}', {value})"
|
|
||||||
)
|
|
||||||
|
|
||||||
def populate_param(
|
def populate_param(
|
||||||
self,
|
self,
|
||||||
|
@ -382,9 +300,21 @@ class Common:
|
||||||
if "variable" in param:
|
if "variable" in param:
|
||||||
if param["variable"].path == self.elt.path:
|
if param["variable"].path == self.elt.path:
|
||||||
return f'ParamSelfInformation("{param["information"]}", {default})'
|
return f'ParamSelfInformation("{param["information"]}", {default})'
|
||||||
return f'ParamInformation("{param["information"]}", {default}, option={self.tiramisu.reflector_objects[param["variable"].path].get(self.calls, self.elt.path)})'
|
information_variable_path = param["variable"].path
|
||||||
|
information_variable = self.tiramisu.reflector_objects[information_variable_path]
|
||||||
|
if information_variable_path not in self.calls:
|
||||||
|
option_name = information_variable.get(self.calls, self.elt.path)
|
||||||
|
return f'ParamInformation("{param["information"]}", {default}, option={option_name})'
|
||||||
|
else:
|
||||||
|
information = f'ParamInformation("{param["information"]}", {default})'
|
||||||
|
information_name = self.tiramisu.get_information_name()
|
||||||
|
self.tiramisu.text["option"].append(f'{information_name} = {information}')
|
||||||
|
information_variable.informations.append(information_name)
|
||||||
|
return information_name
|
||||||
return f'ParamInformation("{param["information"]}", {default})'
|
return f'ParamInformation("{param["information"]}", {default})'
|
||||||
if param["type"] == "suffix":
|
if param["type"] == "suffix":
|
||||||
|
if "suffix" in param and param["suffix"] != None:
|
||||||
|
return f"ParamSuffix(suffix_index={param['suffix']})"
|
||||||
return "ParamSuffix()"
|
return "ParamSuffix()"
|
||||||
if param["type"] == "index":
|
if param["type"] == "index":
|
||||||
return "ParamIndex()"
|
return "ParamIndex()"
|
||||||
|
@ -419,10 +349,7 @@ class Common:
|
||||||
params = [f"{option_name}"]
|
params = [f"{option_name}"]
|
||||||
if suffix is not None:
|
if suffix is not None:
|
||||||
param_type = "ParamDynOption"
|
param_type = "ParamDynOption"
|
||||||
family = self.tiramisu.reflector_objects[dynamic.path].get(
|
params.append(str(suffix))
|
||||||
self.calls, self.elt.path
|
|
||||||
)
|
|
||||||
params.extend([f"'{suffix}'", f"{family}"])
|
|
||||||
else:
|
else:
|
||||||
param_type = "ParamOption"
|
param_type = "ParamOption"
|
||||||
if not propertyerror:
|
if not propertyerror:
|
||||||
|
@ -434,7 +361,6 @@ class Common:
|
||||||
function,
|
function,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Generate calculated value"""
|
"""Generate calculated value"""
|
||||||
self.tiramisu.add_jinja_support()
|
|
||||||
child = function.to_function(self.objectspace)
|
child = function.to_function(self.objectspace)
|
||||||
new_args = []
|
new_args = []
|
||||||
kwargs = []
|
kwargs = []
|
||||||
|
@ -461,6 +387,37 @@ class Common:
|
||||||
ret = ret + ")"
|
ret = ret + ")"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def populate_calculation(self,
|
||||||
|
datas: Union[Calculation, str, list],
|
||||||
|
return_a_tuple: bool=False,
|
||||||
|
) -> str:
|
||||||
|
if isinstance(datas, str):
|
||||||
|
return self.convert_str(datas)
|
||||||
|
if isinstance(datas, Calculation):
|
||||||
|
return self.calculation_value(datas)
|
||||||
|
if not isinstance(datas, list):
|
||||||
|
return datas
|
||||||
|
params = []
|
||||||
|
for idx, data in enumerate(datas):
|
||||||
|
if isinstance(data, Calculation):
|
||||||
|
params.append(self.calculation_value(data))
|
||||||
|
elif isinstance(data, str):
|
||||||
|
params.append(self.convert_str(data))
|
||||||
|
else:
|
||||||
|
params.append(str(data))
|
||||||
|
if return_a_tuple:
|
||||||
|
ret = '('
|
||||||
|
else:
|
||||||
|
ret = '['
|
||||||
|
ret += ", ".join(params)
|
||||||
|
if return_a_tuple:
|
||||||
|
if len(params) <= 1:
|
||||||
|
ret += ","
|
||||||
|
ret += ")"
|
||||||
|
else:
|
||||||
|
ret += "]"
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class Variable(Common):
|
class Variable(Common):
|
||||||
"""Manage variable"""
|
"""Manage variable"""
|
||||||
|
@ -471,6 +428,9 @@ class Variable(Common):
|
||||||
tiramisu,
|
tiramisu,
|
||||||
):
|
):
|
||||||
super().__init__(elt, tiramisu)
|
super().__init__(elt, tiramisu)
|
||||||
|
if elt.type in self.tiramisu.objectspace.rougailconfig['custom_types']:
|
||||||
|
self.object_type = self.tiramisu.objectspace.rougailconfig['custom_types'][elt.type].__name__
|
||||||
|
else:
|
||||||
self.object_type = CONVERT_OPTION[elt.type]["opttype"]
|
self.object_type = CONVERT_OPTION[elt.type]["opttype"]
|
||||||
|
|
||||||
def _populate_attrib(
|
def _populate_attrib(
|
||||||
|
@ -482,57 +442,20 @@ class Variable(Common):
|
||||||
self.calls, self.elt.path
|
self.calls, self.elt.path
|
||||||
)
|
)
|
||||||
if self.elt.type == "choice":
|
if self.elt.type == "choice":
|
||||||
choices = self.elt.choices
|
keys["values"] = self.populate_calculation(self.elt.choices, return_a_tuple=True)
|
||||||
if isinstance(choices, Calculation):
|
|
||||||
keys["values"] = self.calculation_value(choices)
|
|
||||||
else:
|
|
||||||
new_values = []
|
|
||||||
for value in choices:
|
|
||||||
if isinstance(value, Calculation):
|
|
||||||
new_values.append(self.calculation_value(value))
|
|
||||||
elif isinstance(value, str):
|
|
||||||
new_values.append(self.convert_str(value))
|
|
||||||
else:
|
|
||||||
new_values.append(str(value))
|
|
||||||
keys["values"] = "(" + ", ".join(new_values)
|
|
||||||
if len(new_values) <= 1:
|
|
||||||
keys["values"] += ","
|
|
||||||
keys["values"] += ")"
|
|
||||||
if self.elt.path in self.objectspace.multis:
|
if self.elt.path in self.objectspace.multis:
|
||||||
keys["multi"] = self.objectspace.multis[self.elt.path]
|
keys["multi"] = self.objectspace.multis[self.elt.path]
|
||||||
|
if not hasattr(self.elt, "default"):
|
||||||
|
print('FIXME CA EXISTE!!!')
|
||||||
if hasattr(self.elt, "default") and self.elt.default is not None:
|
if hasattr(self.elt, "default") and self.elt.default is not None:
|
||||||
value = self.elt.default
|
keys["default"] = self.populate_calculation(self.elt.default)
|
||||||
if isinstance(value, str):
|
|
||||||
value = self.convert_str(value)
|
|
||||||
elif isinstance(value, Calculation):
|
|
||||||
value = self.calculation_value(value)
|
|
||||||
elif isinstance(value, list):
|
|
||||||
value = value.copy()
|
|
||||||
for idx, val in enumerate(value):
|
|
||||||
if isinstance(val, Calculation):
|
|
||||||
value[idx] = self.calculation_value(val)
|
|
||||||
else:
|
|
||||||
value[idx] = self.convert_str(val)
|
|
||||||
value = "[" + ", ".join(value) + "]"
|
|
||||||
keys["default"] = value
|
|
||||||
if self.elt.path in self.objectspace.default_multi:
|
if self.elt.path in self.objectspace.default_multi:
|
||||||
value = self.objectspace.default_multi[self.elt.path]
|
keys["default_multi"] = self.populate_calculation(self.objectspace.default_multi[self.elt.path])
|
||||||
|
if self.elt.validators:
|
||||||
|
keys["validators"] = self.populate_calculation(self.elt.validators)
|
||||||
|
for key, value in CONVERT_OPTION.get(self.elt.type, {}).get("initkwargs", {}).items():
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
value = self.convert_str(value)
|
value = self.convert_str(value)
|
||||||
elif isinstance(value, Calculation):
|
|
||||||
value = self.calculation_value(value)
|
|
||||||
keys["default_multi"] = value
|
|
||||||
if self.elt.validators:
|
|
||||||
validators = []
|
|
||||||
for val in self.elt.validators:
|
|
||||||
if isinstance(val, Calculation):
|
|
||||||
validators.append(self.calculation_value(val))
|
|
||||||
else:
|
|
||||||
validators.append(val)
|
|
||||||
keys["validators"] = "[" + ", ".join(validators) + "]"
|
|
||||||
for key, value in CONVERT_OPTION[self.elt.type].get("initkwargs", {}).items():
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = f"'{value}'"
|
|
||||||
keys[key] = value
|
keys[key] = value
|
||||||
if self.elt.params:
|
if self.elt.params:
|
||||||
for param in self.elt.params:
|
for param in self.elt.params:
|
||||||
|
@ -569,12 +492,7 @@ class Family(Common):
|
||||||
keys: list,
|
keys: list,
|
||||||
) -> None:
|
) -> None:
|
||||||
if self.elt.type == "dynamic":
|
if self.elt.type == "dynamic":
|
||||||
dyn = self.tiramisu.reflector_objects[self.elt.variable.path].get(
|
keys["suffixes"] = self.populate_calculation(self.elt.dynamic)
|
||||||
self.calls, self.elt.path
|
|
||||||
)
|
|
||||||
keys[
|
|
||||||
"suffixes"
|
|
||||||
] = f"Calculation(func['calc_value'], Params((ParamOption({dyn}, notraisepropertyerror=True))))"
|
|
||||||
children = []
|
children = []
|
||||||
for path in self.objectspace.parents[self.elt.path]:
|
for path in self.objectspace.parents[self.elt.path]:
|
||||||
children.append(self.objectspace.paths[path])
|
children.append(self.objectspace.paths[path])
|
||||||
|
|
|
@ -23,38 +23,29 @@ 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 List, Any, Optional, Tuple
|
|
||||||
from os.path import join, isfile, isdir, basename
|
|
||||||
from os import listdir, makedirs
|
from os import listdir, makedirs
|
||||||
|
from os.path import basename, isdir, isfile, join
|
||||||
|
from typing import Any, List, Optional, Tuple
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from lxml.etree import parse, XMLParser, XMLSyntaxError # pylint: disable=E0611
|
from lxml.etree import SubElement # pylint: disable=E0611
|
||||||
from lxml.etree import Element, SubElement, tostring
|
from lxml.etree import Element, XMLParser, XMLSyntaxError, parse, tostring
|
||||||
except ModuleNotFoundError as err:
|
except ModuleNotFoundError as err:
|
||||||
parse = None
|
parse = None
|
||||||
|
|
||||||
# from ast import parse as ast_parse
|
# from ast import parse as ast_parse
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from yaml import safe_load, dump, SafeDumper
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .i18n import _
|
from ruamel.yaml import YAML
|
||||||
from .error import UpgradeError
|
|
||||||
|
|
||||||
from .utils import normalize_family
|
|
||||||
from .config import RougailConfig
|
from .config import RougailConfig
|
||||||
from .annotator.variable import CONVERT_OPTION
|
from .error import UpgradeError
|
||||||
|
from .i18n import _
|
||||||
|
from .object_model import CONVERT_OPTION
|
||||||
|
from .utils import normalize_family
|
||||||
|
|
||||||
|
VERSIONS = ["0.10", "1.0", "1.1"]
|
||||||
VERSIONS = ["0.10", "1.0"]
|
|
||||||
|
|
||||||
FIXME_PRINT_FILENAME = True
|
|
||||||
FIXME_PRINT_FILENAME = False
|
|
||||||
FIXME_PRINT_FILE = True
|
|
||||||
FIXME_PRINT_FILE = False
|
|
||||||
FIXME_PRINT_UNKNOWN_VAR = True
|
|
||||||
FIXME_PRINT_UNKNOWN_VAR = False
|
|
||||||
FIXME_PRINT_REMOVE = True
|
|
||||||
FIXME_PRINT_REMOVE = False
|
|
||||||
|
|
||||||
|
|
||||||
def get_function_name(version):
|
def get_function_name(version):
|
||||||
|
@ -65,22 +56,13 @@ def get_function_name(version):
|
||||||
FUNCTION_VERSIONS = [(version, get_function_name(version)) for version in VERSIONS]
|
FUNCTION_VERSIONS = [(version, get_function_name(version)) for version in VERSIONS]
|
||||||
|
|
||||||
|
|
||||||
class NoAliasDumper(SafeDumper):
|
class upgrade_010_to_10:
|
||||||
def ignore_aliases(self, data):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class upgrade_010_to_100:
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
dico: dict,
|
dico: dict,
|
||||||
namespace: str,
|
namespace: str,
|
||||||
xmlsrc: str,
|
xmlsrc: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
if FIXME_PRINT_FILE:
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
pprint(dico)
|
|
||||||
self.xmlsrc = xmlsrc
|
self.xmlsrc = xmlsrc
|
||||||
self.paths = {"family": {}, "variable": {}}
|
self.paths = {"family": {}, "variable": {}}
|
||||||
self.lists = {
|
self.lists = {
|
||||||
|
@ -94,10 +76,6 @@ class upgrade_010_to_100:
|
||||||
self.parse_variables_with_path()
|
self.parse_variables_with_path()
|
||||||
self.parse_services(dico)
|
self.parse_services(dico)
|
||||||
self.parse_constraints(dico)
|
self.parse_constraints(dico)
|
||||||
if FIXME_PRINT_FILE:
|
|
||||||
print("==")
|
|
||||||
pprint(self.variables)
|
|
||||||
pprint(self.services)
|
|
||||||
|
|
||||||
def parse_variables(
|
def parse_variables(
|
||||||
self,
|
self,
|
||||||
|
@ -159,13 +137,14 @@ class upgrade_010_to_100:
|
||||||
new_families[name] = variable
|
new_families[name] = variable
|
||||||
self.flatten_paths["variable"][name] = sub_path
|
self.flatten_paths["variable"][name] = sub_path
|
||||||
self.paths["variable"][sub_path] = variable
|
self.paths["variable"][sub_path] = variable
|
||||||
if "redefine" not in variable and "value" not in variable and "mandatory" not in variable and ("type" not in variable or variable["type"] != "boolean"):
|
if (
|
||||||
|
"redefine" not in variable
|
||||||
|
and "value" not in variable
|
||||||
|
and "mandatory" not in variable
|
||||||
|
and ("type" not in variable or variable["type"] != "boolean")
|
||||||
|
):
|
||||||
variable["mandatory"] = False
|
variable["mandatory"] = False
|
||||||
if "remove_condition" in variable and variable.pop("remove_condition"):
|
if "remove_condition" in variable and variable.pop("remove_condition"):
|
||||||
if FIXME_PRINT_REMOVE:
|
|
||||||
print(
|
|
||||||
f"variable {name} in file {self.xmlsrc} has remove_condition, all properties (hidden, disabled and mandatory) are set to False"
|
|
||||||
)
|
|
||||||
for prop in ["hidden", "disabled", "mandatory"]:
|
for prop in ["hidden", "disabled", "mandatory"]:
|
||||||
if prop not in variable:
|
if prop not in variable:
|
||||||
variable[prop] = False
|
variable[prop] = False
|
||||||
|
@ -334,27 +313,19 @@ class upgrade_010_to_100:
|
||||||
condition_value = True
|
condition_value = True
|
||||||
else:
|
else:
|
||||||
condition_value = self.params_condition_to_jinja(
|
condition_value = self.params_condition_to_jinja(
|
||||||
source, condition["param"], name.endswith("if_in")
|
prop, source, condition["param"], name.endswith("if_in")
|
||||||
)
|
)
|
||||||
for target in condition["target"]:
|
for target in condition["target"]:
|
||||||
typ = target.get("type", "variable")
|
typ = target.get("type", "variable")
|
||||||
if typ == "variable":
|
if typ == "variable":
|
||||||
variable_path = self.get_variable_path(target["text"])
|
variable_path = self.get_variable_path(target["text"])
|
||||||
if variable_path is None:
|
if variable_path is None:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR and not target.get("optional", False):
|
|
||||||
print(
|
|
||||||
f'pffff la target {target["text"]} de la condition n\'est pas trouvable'
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
variable = self.paths["variable"][variable_path]
|
variable = self.paths["variable"][variable_path]
|
||||||
variable[prop] = condition_value
|
variable[prop] = condition_value
|
||||||
elif typ == "family":
|
elif typ == "family":
|
||||||
family_path = self.get_family_path(target["text"])
|
family_path = self.get_family_path(target["text"])
|
||||||
if family_path is None:
|
if family_path is None:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR and not target.get("optional", False):
|
|
||||||
print(
|
|
||||||
f'pffff la target {target["text"]} de la condition n\'est pas trouvable'
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
family = self.paths["family"][family_path]
|
family = self.paths["family"][family_path]
|
||||||
family[prop] = condition_value
|
family[prop] = condition_value
|
||||||
|
@ -386,10 +357,6 @@ class upgrade_010_to_100:
|
||||||
for target in check["target"]:
|
for target in check["target"]:
|
||||||
variable_path = self.get_variable_path(target["text"])
|
variable_path = self.get_variable_path(target["text"])
|
||||||
if variable_path is None:
|
if variable_path is None:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR and not target.get("optional", False):
|
|
||||||
print(
|
|
||||||
f'pffff la target {target["text"]} dans le check n\'est pas trouvable'
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
variable = self.paths["variable"][variable_path]
|
variable = self.paths["variable"][variable_path]
|
||||||
if "validators" in variable and variable["validators"] is None:
|
if "validators" in variable and variable["validators"] is None:
|
||||||
|
@ -411,10 +378,6 @@ class upgrade_010_to_100:
|
||||||
params = []
|
params = []
|
||||||
variable_path = self.get_variable_path(target["text"])
|
variable_path = self.get_variable_path(target["text"])
|
||||||
if variable_path is None:
|
if variable_path is None:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR and not target.get("optional", False):
|
|
||||||
print(
|
|
||||||
f'pffff la target {target["text"]} dans le fill n\'est pas trouvable'
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
variable = self.paths["variable"][variable_path]
|
variable = self.paths["variable"][variable_path]
|
||||||
if fill.get("type") == "jinja":
|
if fill.get("type") == "jinja":
|
||||||
|
@ -428,6 +391,7 @@ class upgrade_010_to_100:
|
||||||
|
|
||||||
def params_condition_to_jinja(
|
def params_condition_to_jinja(
|
||||||
self,
|
self,
|
||||||
|
prop: str,
|
||||||
path: str,
|
path: str,
|
||||||
params: List[dict],
|
params: List[dict],
|
||||||
if_in: bool,
|
if_in: bool,
|
||||||
|
@ -444,9 +408,9 @@ class upgrade_010_to_100:
|
||||||
if new_param:
|
if new_param:
|
||||||
new_params |= new_param
|
new_params |= new_param
|
||||||
if if_in:
|
if if_in:
|
||||||
jinja += " %}true{% else %}false{% endif %}"
|
jinja += " %}" + prop + "{% endif %}"
|
||||||
else:
|
else:
|
||||||
jinja += " %}false{% else %}true{% endif %}"
|
jinja += " %}{% else %}" + prop + "{% endif %}"
|
||||||
ret = {
|
ret = {
|
||||||
"type": "jinja",
|
"type": "jinja",
|
||||||
"jinja": jinja,
|
"jinja": jinja,
|
||||||
|
@ -459,11 +423,9 @@ class upgrade_010_to_100:
|
||||||
self,
|
self,
|
||||||
param: dict,
|
param: dict,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
# <!ATTLIST type (string|number|nil|space|boolean|variable|function|information|suffix|index) "string">
|
|
||||||
typ = param.get("type", "string")
|
typ = param.get("type", "string")
|
||||||
if typ == "string":
|
if typ == "string":
|
||||||
value = param["text"]
|
value = param["text"]
|
||||||
# value = dumps(value, ensure_ascii=False)
|
|
||||||
elif typ == "number":
|
elif typ == "number":
|
||||||
value = int(param["text"])
|
value = int(param["text"])
|
||||||
elif typ == "nil":
|
elif typ == "nil":
|
||||||
|
@ -551,7 +513,6 @@ class upgrade_010_to_100:
|
||||||
new_param, value = self.get_jinja_param_and_value(param)
|
new_param, value = self.get_jinja_param_and_value(param)
|
||||||
if new_param:
|
if new_param:
|
||||||
params |= new_param
|
params |= new_param
|
||||||
# if param.get('type') != 'variable' or value is not None:
|
|
||||||
if "name" in param:
|
if "name" in param:
|
||||||
values.append(f'{param["name"]}={value}')
|
values.append(f'{param["name"]}={value}')
|
||||||
else:
|
else:
|
||||||
|
@ -577,8 +538,6 @@ class upgrade_010_to_100:
|
||||||
):
|
):
|
||||||
path = self.flatten_paths["variable"][path]
|
path = self.flatten_paths["variable"][path]
|
||||||
if path not in self.paths["variable"]:
|
if path not in self.paths["variable"]:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR:
|
|
||||||
print("pffff impossible de trouver la variable", path)
|
|
||||||
return
|
return
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -589,8 +548,6 @@ class upgrade_010_to_100:
|
||||||
if path not in self.paths["family"] and path in self.flatten_paths["family"]:
|
if path not in self.paths["family"] and path in self.flatten_paths["family"]:
|
||||||
path = self.flatten_paths["family"][path]
|
path = self.flatten_paths["family"][path]
|
||||||
if path not in self.paths["family"]:
|
if path not in self.paths["family"]:
|
||||||
if FIXME_PRINT_UNKNOWN_VAR:
|
|
||||||
print("pffff impossible de trouver la famille", path)
|
|
||||||
return
|
return
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -615,17 +572,18 @@ class RougailUpgrade:
|
||||||
|
|
||||||
def load_dictionaries(
|
def load_dictionaries(
|
||||||
self,
|
self,
|
||||||
# srcfolder: str,
|
|
||||||
dstfolder: str,
|
dstfolder: str,
|
||||||
services_dstfolder: Optional[str],
|
services_dstfolder: Optional[str] = None,
|
||||||
extra_dstfolder: Optional[str] = None,
|
extra_dstfolder: Optional[str] = None,
|
||||||
# namespace: str,
|
|
||||||
# display: bool=True,
|
|
||||||
):
|
):
|
||||||
if extra_dstfolder is None:
|
if extra_dstfolder is None:
|
||||||
extra_dstfolder = dstfolder
|
extra_dstfolder = dstfolder
|
||||||
|
dict_dirs = self.rougailconfig["dictionaries_dir"]
|
||||||
|
if not isinstance(dict_dirs, list):
|
||||||
|
dict_dirs = [dict_dirs]
|
||||||
|
for dict_dir in dict_dirs:
|
||||||
self._load_dictionaries(
|
self._load_dictionaries(
|
||||||
self.rougailconfig["dictionaries_dir"],
|
dict_dir,
|
||||||
dstfolder,
|
dstfolder,
|
||||||
services_dstfolder,
|
services_dstfolder,
|
||||||
self.rougailconfig["variable_namespace"],
|
self.rougailconfig["variable_namespace"],
|
||||||
|
@ -671,7 +629,7 @@ class RougailUpgrade:
|
||||||
)
|
)
|
||||||
if filename.endswith(".xml"):
|
if filename.endswith(".xml"):
|
||||||
if parse is None:
|
if parse is None:
|
||||||
raise Exception('XML module is not installed')
|
raise Exception("XML module is not installed")
|
||||||
try:
|
try:
|
||||||
parser = XMLParser(remove_blank_text=True)
|
parser = XMLParser(remove_blank_text=True)
|
||||||
document = parse(xmlsrc, parser)
|
document = parse(xmlsrc, parser)
|
||||||
|
@ -684,61 +642,44 @@ class RougailUpgrade:
|
||||||
ext = "xml"
|
ext = "xml"
|
||||||
else:
|
else:
|
||||||
with xmlsrc.open() as xml_fh:
|
with xmlsrc.open() as xml_fh:
|
||||||
root = safe_load(xml_fh)
|
root = YAML(typ="safe").load(file_fh)
|
||||||
search_function_name = get_function_name(root["version"])
|
search_function_name = get_function_name(str(root["version"]))
|
||||||
ext = "yml"
|
ext = "yml"
|
||||||
function_found = False
|
function_found = False
|
||||||
if FIXME_PRINT_FILENAME:
|
root_services = None
|
||||||
print(
|
|
||||||
"========================================================================"
|
|
||||||
)
|
|
||||||
print(xmlsrc)
|
|
||||||
print(
|
|
||||||
"========================================================================"
|
|
||||||
)
|
|
||||||
for version, function_version in FUNCTION_VERSIONS:
|
for version, function_version in FUNCTION_VERSIONS:
|
||||||
if function_found and hasattr(self, function_version):
|
if function_found and hasattr(self, function_version):
|
||||||
# if display:
|
|
||||||
# print(f' - convert {filename} to version {version}')
|
|
||||||
upgrade_help = self.upgrade_help.get(function_version, {}).get(
|
upgrade_help = self.upgrade_help.get(function_version, {}).get(
|
||||||
filename, {}
|
filename, {}
|
||||||
)
|
)
|
||||||
if upgrade_help.get("remove") is True:
|
if upgrade_help.get("remove") is True:
|
||||||
continue
|
continue
|
||||||
root, root_services, new_type = getattr(self, function_version)(
|
root, root_services_, new_type = getattr(self, function_version)(
|
||||||
root, upgrade_help, namespace, xmlsrc, ext
|
root, upgrade_help, namespace, xmlsrc, ext
|
||||||
)
|
)
|
||||||
|
if root_services_ is not None:
|
||||||
|
root_services = root_services_
|
||||||
if function_version == search_function_name:
|
if function_version == search_function_name:
|
||||||
function_found = True
|
function_found = True
|
||||||
if root:
|
if root:
|
||||||
root["version"] = version
|
root["version"] = version
|
||||||
xmldst.parent.mkdir(parents=True, exist_ok=True)
|
xmldst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with xmldst.open("w") as ymlfh:
|
with xmldst.open("w") as ymlfh:
|
||||||
dump(
|
yaml = YAML()
|
||||||
|
yaml.dump(
|
||||||
root,
|
root,
|
||||||
ymlfh,
|
xmldst,
|
||||||
allow_unicode=True,
|
|
||||||
sort_keys=False,
|
|
||||||
Dumper=NoAliasDumper,
|
|
||||||
)
|
)
|
||||||
if root_services and services_dstfolder:
|
if root_services and services_dstfolder:
|
||||||
root_services["version"] = version
|
root_services["version"] = version
|
||||||
ymldst_services.parent.mkdir(parents=True, exist_ok=True)
|
ymldst_services.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with ymldst_services.open("w") as ymlfh:
|
with ymldst_services.open("w") as ymlfh:
|
||||||
dump(
|
yaml = YAML()
|
||||||
|
yaml.dump(
|
||||||
root_services,
|
root_services,
|
||||||
ymlfh,
|
ymlfh,
|
||||||
allow_unicode=True,
|
|
||||||
sort_keys=False,
|
|
||||||
Dumper=NoAliasDumper,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# if not self.dtd.validate(document):
|
|
||||||
# dtd_error = self.dtd.error_log.filter_from_errors()[0]
|
|
||||||
# msg = _(f'not a valid XML file: {dtd_error}')
|
|
||||||
# raise DictConsistencyError(msg, 43, [xmlfile])
|
|
||||||
# yield xmlfile, document.getroot()
|
|
||||||
|
|
||||||
def _attribut_to_bool(self, variable):
|
def _attribut_to_bool(self, variable):
|
||||||
for prop in [
|
for prop in [
|
||||||
"mandatory",
|
"mandatory",
|
||||||
|
@ -766,9 +707,10 @@ class RougailUpgrade:
|
||||||
def _attribut_to_int(self, variable):
|
def _attribut_to_int(self, variable):
|
||||||
for prop in ["mode"]:
|
for prop in ["mode"]:
|
||||||
if prop in variable:
|
if prop in variable:
|
||||||
if variable[prop] in ['expert', 'normal']:
|
if variable[prop] in ["expert", "normal"]:
|
||||||
variable[prop] = {'expert': 'advanced',
|
variable[prop] = {
|
||||||
'normal': 'standard',
|
"expert": "advanced",
|
||||||
|
"normal": "standard",
|
||||||
}.get(variable[prop])
|
}.get(variable[prop])
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
@ -891,6 +833,36 @@ class RougailUpgrade:
|
||||||
dico = {obj_name: dico}
|
dico = {obj_name: dico}
|
||||||
return dico
|
return dico
|
||||||
|
|
||||||
|
def _update_1_1(self, root):
|
||||||
|
if not isinstance(root, dict):
|
||||||
|
return
|
||||||
|
# migrate dynamic family
|
||||||
|
if (
|
||||||
|
("variable" in root and isinstance(root["variable"], str))
|
||||||
|
or ("_variable" in root and isinstance(root["_variable"], str))
|
||||||
|
) and (
|
||||||
|
("_type" in root and root["_type"] == "dynamic")
|
||||||
|
or ("type" in root and root["type"] == "dynamic")
|
||||||
|
):
|
||||||
|
root["dynamic"] = {
|
||||||
|
"type": "variable",
|
||||||
|
"variable": root.pop("variable"),
|
||||||
|
"propertyerror": False,
|
||||||
|
}
|
||||||
|
for key, value in root.items():
|
||||||
|
self._update_1_1(value)
|
||||||
|
|
||||||
|
def update_1_1(
|
||||||
|
self,
|
||||||
|
root,
|
||||||
|
upgrade_help: dict,
|
||||||
|
namespace: str,
|
||||||
|
xmlsrc: str,
|
||||||
|
ext: str,
|
||||||
|
):
|
||||||
|
self._update_1_1(root)
|
||||||
|
return root, None, "yml"
|
||||||
|
|
||||||
def update_1_0(
|
def update_1_0(
|
||||||
self,
|
self,
|
||||||
root: "Element",
|
root: "Element",
|
||||||
|
@ -909,19 +881,9 @@ class RougailUpgrade:
|
||||||
new_objects = self._xml_to_yaml(objects, typ, variables, "")
|
new_objects = self._xml_to_yaml(objects, typ, variables, "")
|
||||||
if new_objects[typ]:
|
if new_objects[typ]:
|
||||||
new_root.update(new_objects)
|
new_root.update(new_objects)
|
||||||
# services = root.find('services')
|
|
||||||
# if services is None:
|
|
||||||
# services = []
|
|
||||||
# new_services = self._xml_to_yaml_service(services)
|
|
||||||
# if new_services:
|
|
||||||
# new_root['services'] = new_services
|
|
||||||
# paths = self._get_path_variables(variables,
|
|
||||||
# namespace == 'configuration',
|
|
||||||
# namespace,
|
|
||||||
# )
|
|
||||||
else:
|
else:
|
||||||
new_root = root
|
new_root = root
|
||||||
variables, services = upgrade_010_to_100(new_root, namespace, xmlsrc).get()
|
variables, services = upgrade_010_to_10(new_root, namespace, xmlsrc).get()
|
||||||
return variables, services, "yml"
|
return variables, services, "yml"
|
||||||
|
|
||||||
def update_0_10(
|
def update_0_10(
|
||||||
|
|
|
@ -87,11 +87,14 @@ def get_realpath(
|
||||||
|
|
||||||
|
|
||||||
def get_jinja_variable_to_param(
|
def get_jinja_variable_to_param(
|
||||||
|
current_path: str,
|
||||||
jinja_text,
|
jinja_text,
|
||||||
objectspace,
|
objectspace,
|
||||||
xmlfiles,
|
xmlfiles,
|
||||||
functions,
|
functions,
|
||||||
path_prefix,
|
path_prefix,
|
||||||
|
version,
|
||||||
|
namespace,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
env = SandboxedEnvironment(loader=DictLoader({"tmpl": jinja_text}))
|
env = SandboxedEnvironment(loader=DictLoader({"tmpl": jinja_text}))
|
||||||
|
@ -112,8 +115,13 @@ def get_jinja_variable_to_param(
|
||||||
variables = list(variables)
|
variables = list(variables)
|
||||||
variables.sort()
|
variables.sort()
|
||||||
for variable_path in variables:
|
for variable_path in variables:
|
||||||
variable, suffix, dynamic = objectspace.paths.get_with_dynamic(
|
variable, suffix = objectspace.paths.get_with_dynamic(
|
||||||
get_realpath(variable_path, path_prefix)
|
variable_path,
|
||||||
|
path_prefix,
|
||||||
|
current_path,
|
||||||
|
version,
|
||||||
|
namespace,
|
||||||
|
xmlfiles,
|
||||||
)
|
)
|
||||||
if variable and variable.path in objectspace.variables:
|
if variable and variable.path in objectspace.variables:
|
||||||
yield variable, suffix, variable_path, dynamic
|
yield variable, suffix, variable_path
|
||||||
|
|
7
tests/custom.py
Normal file
7
tests/custom.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from os import environ
|
||||||
|
environ['TIRAMISU_LOCALE'] = 'en'
|
||||||
|
from tiramisu import StrOption
|
||||||
|
|
||||||
|
|
||||||
|
class CustomOption(StrOption):
|
||||||
|
pass
|
5
tests/data/dict1/dict.yml
Normal file
5
tests/data/dict1/dict.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# dict with a correct version declared
|
||||||
|
#version: "1.1"
|
||||||
|
hello:
|
||||||
|
type: string
|
||||||
|
default: world
|
5
tests/data/dict2/dict.yml
Normal file
5
tests/data/dict2/dict.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# dict with a correct version declared
|
||||||
|
version: "1.0"
|
||||||
|
hello:
|
||||||
|
type: string
|
||||||
|
default: world
|
|
@ -1,22 +1,8 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"}))
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"}))
|
||||||
optiondescription_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
|
optiondescription_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
---
|
||||||
version: '1.0'
|
version: '1.0'
|
||||||
my_family:
|
my_family:
|
||||||
|
type: family
|
||||||
|
|
1
tests/dictionaries/00empty_family/makedict/after.json
Normal file
1
tests/dictionaries/00empty_family/makedict/after.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
tests/dictionaries/00empty_family/makedict/base.json
Normal file
1
tests/dictionaries/00empty_family/makedict/base.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
tests/dictionaries/00empty_family/makedict/before.json
Normal file
1
tests/dictionaries/00empty_family/makedict/before.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
9
tests/dictionaries/00empty_family/tiramisu/base.py
Normal file
9
tests/dictionaries/00empty_family/tiramisu/base.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[], properties=frozenset({"advanced"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
10
tests/dictionaries/00empty_family/tiramisu/multi.py
Normal file
10
tests/dictionaries/00empty_family/tiramisu/multi.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -1,3 +1,5 @@
|
||||||
|
---
|
||||||
version: '1.0'
|
version: '1.0'
|
||||||
my_family:
|
my_family:
|
||||||
my_sub_family:
|
my_sub_family:
|
||||||
|
type: family
|
||||||
|
|
1
tests/dictionaries/00empty_subfamily/makedict/after.json
Normal file
1
tests/dictionaries/00empty_subfamily/makedict/after.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
tests/dictionaries/00empty_subfamily/makedict/base.json
Normal file
1
tests/dictionaries/00empty_subfamily/makedict/base.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
9
tests/dictionaries/00empty_subfamily/tiramisu/base.py
Normal file
9
tests/dictionaries/00empty_subfamily/tiramisu/base.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[], properties=frozenset({"advanced"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
10
tests/dictionaries/00empty_subfamily/tiramisu/multi.py
Normal file
10
tests/dictionaries/00empty_subfamily/tiramisu/multi.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"advanced"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_2 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}))
|
option_2 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}))
|
||||||
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"basic"}))
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"basic"}))
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_3 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}))
|
option_3 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}))
|
||||||
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"basic"}))
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"basic"}))
|
||||||
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
option_4 = StrOption(name="without_type", doc="without_type", default="non", properties=frozenset({"mandatory", "standard"}))
|
option_4 = StrOption(name="without_type", doc="without_type", default="non", properties=frozenset({"mandatory", "standard"}))
|
||||||
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"standard"}))
|
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
option_5 = StrOption(name="without_type", doc="without_type", default="non", properties=frozenset({"mandatory", "standard"}))
|
option_5 = StrOption(name="without_type", doc="without_type", default="non", properties=frozenset({"mandatory", "standard"}))
|
||||||
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4, option_5], properties=frozenset({"standard"}))
|
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4, option_5], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"standard"}))
|
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"standard"}))
|
||||||
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[optiondescription_2], properties=frozenset({"standard"}))
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4], properties=frozenset({"standard"}))
|
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4], properties=frozenset({"standard"}))
|
||||||
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[optiondescription_3], properties=frozenset({"standard"}))
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[optiondescription_3], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
option_4 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_4 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"standard"}))
|
optiondescription_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
from tiramisu import *
|
from tiramisu import *
|
||||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("standard")
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
|
||||||
global func
|
|
||||||
func = {'calc_value': calc_value}
|
|
||||||
|
|
||||||
def _load_functions(path):
|
|
||||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
|
||||||
loader = _SourceFileLoader('func', path)
|
|
||||||
spec = _spec_from_loader(loader.name, loader)
|
|
||||||
func_ = _module_from_spec(spec)
|
|
||||||
loader.exec_module(func_)
|
|
||||||
for function in dir(func_):
|
|
||||||
if function.startswith('_'):
|
|
||||||
continue
|
|
||||||
func[function] = getattr(func_, function)
|
|
||||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
|
||||||
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
option_5 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
option_5 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "standard"}))
|
||||||
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4, option_5], properties=frozenset({"standard"}))
|
optiondescription_3 = OptionDescription(name="general", doc="général", children=[option_4, option_5], properties=frozenset({"standard"}))
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default: true
|
6
tests/dictionaries/01base_auto_bool/makedict/after.json
Normal file
6
tests/dictionaries/01base_auto_bool/makedict/after.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
3
tests/dictionaries/01base_auto_bool/makedict/base.json
Normal file
3
tests/dictionaries/01base_auto_bool/makedict/base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": true
|
||||||
|
}
|
6
tests/dictionaries/01base_auto_bool/makedict/before.json
Normal file
6
tests/dictionaries/01base_auto_bool/makedict/before.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
10
tests/dictionaries/01base_auto_bool/tiramisu/base.py
Normal file
10
tests/dictionaries/01base_auto_bool/tiramisu/base.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_2 = BoolOption(name="auto", doc="auto", default=True, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
14
tests/dictionaries/01base_auto_bool/tiramisu/multi.py
Normal file
14
tests/dictionaries/01base_auto_bool/tiramisu/multi.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_3 = BoolOption(name="auto", doc="auto", default=True, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
option_6 = BoolOption(name="auto", doc="auto", default=True, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default:
|
||||||
|
- true
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [true]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": [true]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [true]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
10
tests/dictionaries/01base_auto_bool_multi/tiramisu/base.py
Normal file
10
tests/dictionaries/01base_auto_bool_multi/tiramisu/base.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_2 = BoolOption(name="auto", doc="auto", multi=True, default=[True], default_multi=True, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
14
tests/dictionaries/01base_auto_bool_multi/tiramisu/multi.py
Normal file
14
tests/dictionaries/01base_auto_bool_multi/tiramisu/multi.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_3 = BoolOption(name="auto", doc="auto", multi=True, default=[True], default_multi=True, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
option_6 = BoolOption(name="auto", doc="auto", multi=True, default=[True], default_multi=True, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default: 1.0
|
6
tests/dictionaries/01base_auto_float/makedict/after.json
Normal file
6
tests/dictionaries/01base_auto_float/makedict/after.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
}
|
3
tests/dictionaries/01base_auto_float/makedict/base.json
Normal file
3
tests/dictionaries/01base_auto_float/makedict/base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": 1.0
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
10
tests/dictionaries/01base_auto_float/tiramisu/base.py
Normal file
10
tests/dictionaries/01base_auto_float/tiramisu/base.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_2 = FloatOption(name="auto", doc="auto", default=1.0, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
14
tests/dictionaries/01base_auto_float/tiramisu/multi.py
Normal file
14
tests/dictionaries/01base_auto_float/tiramisu/multi.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_3 = FloatOption(name="auto", doc="auto", default=1.0, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
option_6 = FloatOption(name="auto", doc="auto", default=1.0, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default:
|
||||||
|
- 1.0
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [1.0]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": [1.0]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [1.0]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
10
tests/dictionaries/01base_auto_float_multi/tiramisu/base.py
Normal file
10
tests/dictionaries/01base_auto_float_multi/tiramisu/base.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_2 = FloatOption(name="auto", doc="auto", multi=True, default=[1.0], default_multi=1.0, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
14
tests/dictionaries/01base_auto_float_multi/tiramisu/multi.py
Normal file
14
tests/dictionaries/01base_auto_float_multi/tiramisu/multi.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_3 = FloatOption(name="auto", doc="auto", multi=True, default=[1.0], default_multi=1.0, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
option_6 = FloatOption(name="auto", doc="auto", multi=True, default=[1.0], default_multi=1.0, properties=frozenset({"mandatory", "notempty", "standard"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default: 1
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
}
|
3
tests/dictionaries/01base_auto_number/makedict/base.json
Normal file
3
tests/dictionaries/01base_auto_number/makedict/base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": 1
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
10
tests/dictionaries/01base_auto_number/tiramisu/base.py
Normal file
10
tests/dictionaries/01base_auto_number/tiramisu/base.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_2 = IntOption(name="auto", doc="auto", default=1, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
14
tests/dictionaries/01base_auto_number/tiramisu/multi.py
Normal file
14
tests/dictionaries/01base_auto_number/tiramisu/multi.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from tiramisu import *
|
||||||
|
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||||
|
from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription
|
||||||
|
load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("standard")
|
||||||
|
ALLOWED_LEADER_PROPERTIES.add("advanced")
|
||||||
|
option_3 = IntOption(name="auto", doc="auto", default=1, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
|
||||||
|
option_6 = IntOption(name="auto", doc="auto", default=1, properties=frozenset({"mandatory", "standard"}))
|
||||||
|
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"standard"}))
|
||||||
|
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"standard"}))
|
||||||
|
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
version: '1.1'
|
||||||
|
auto:
|
||||||
|
default:
|
||||||
|
- 1
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [1]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": [1]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rougail.auto": {
|
||||||
|
"owner": "default",
|
||||||
|
"value": [1]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue