Compare commits

...

7 commits

540 changed files with 1677 additions and 523 deletions

13
CHANGELOG.md Normal file
View file

@ -0,0 +1,13 @@
## 1.0.2 (2024-01-28)
### Fix
- 2023 => 2024
- correction for calculated variable with a variable in a dynamic family
## 1.0.1 (2024-01-28)
### Feat
- documentation
- new format 1.0

View file

@ -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).
@ -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.

View file

@ -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:
@ -139,3 +140,54 @@ 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.0'
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?

View file

@ -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
-------------------------------------- --------------------------------------

40
pyproject.toml Normal file
View file

@ -0,0 +1,40 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail"
version = "1.0.2"
authors = [
{name = "Emmanuel Garette", email = "gnunux@gnunux.info"},
]
description = "A consistency handling system that was initially designed in the configuration management"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
"pyyaml ~= 6.0.1",
"pydantic ~= 2.5.2",
"jinja2 ~= 3.1.2",
]
[project.optional-dependancies]
dev = [
"pylint ~= 3.0.3",
]
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "semver"
version_provider = "pep621"
update_changelog_on_bump = true

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -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:
@ -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")

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -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",)

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -33,58 +33,6 @@ from rougail.error import DictConsistencyError
from rougail.object_model import Calculation from rougail.object_model import Calculation
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:
"""Walk to objectspace to find variable or family""" """Walk to objectspace to find variable or family"""

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -77,4 +77,5 @@ RougailConfig = {
"force_convert_dyn_option_description": False, "force_convert_dyn_option_description": False,
"suffix": "", "suffix": "",
"tiramisu_cache": None, "tiramisu_cache": None,
"custom_types": {},
} }

View file

@ -1,4 +1,4 @@
"""Takes a bunch of Rougail XML dispatched in differents folders """Takes a bunch of Rougail YAML dispatched in differents folders
as an input and outputs a Tiramisu's file. as an input and outputs a Tiramisu's file.
Created by: Created by:
@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -27,26 +27,22 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License 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
Sample usage::
>>> from rougail import RougailConvert
>>> rougail = RougailConvert()
>>> tiramisu = rougail.save('tiramisu.py')
The Rougail
- loads the XML into an internal RougailObjSpace representation
- visits/annotates the objects
- dumps the object space as Tiramisu string
The visit/annotation stage is a complex step that corresponds to the Rougail
procedures.
""" """
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator, Tuple from typing import (
Optional,
Union,
get_type_hints,
Any,
Literal,
List,
Dict,
Iterator,
Tuple,
)
from itertools import chain from itertools import chain
from re import findall
from yaml import safe_load from yaml import safe_load
from pydantic import ValidationError from pydantic import ValidationError
@ -56,13 +52,15 @@ from .annotator import SpaceAnnotator
from .tiramisureflector import TiramisuReflector from .tiramisureflector import TiramisuReflector
from .utils import get_realpath from .utils import get_realpath
from .object_model import ( from .object_model import (
CONVERT_OPTION,
Family, Family,
Dynamic, Dynamic,
Variable, _Variable,
Choice, Choice,
SymLink, SymLink,
CALCULATION_TYPES, CALCULATION_TYPES,
Calculation, Calculation,
VariableCalculation,
PARAM_TYPES, PARAM_TYPES,
AnyParam, AnyParam,
) )
@ -100,7 +98,7 @@ class Property:
class Paths: class Paths:
def __init__(self) -> None: def __init__(self) -> None:
self._data: Dict[str, Union[Variable, Family]] = {} self._data: Dict[str, Union[_Variable, Family]] = {}
self._dynamics: List[str] = [] self._dynamics: List[str] = []
self.path_prefix = None self.path_prefix = None
@ -124,9 +122,28 @@ class Paths:
) -> Any: ) -> Any:
suffix = None suffix = None
dynamic_path = None dynamic_path = None
dynamic_variable_path = None
if not path in self._data: if not path in self._data:
for dynamic in self._dynamics: for dynamic in self._dynamics:
if path.startswith(dynamic): if "{{ suffix }}" in dynamic:
regexp = "^" + dynamic.replace('{{ suffix }}', '(.*)') + '.'
finded = findall(regexp, path)
if len(finded) != 1:
continue
splitted_dynamic = dynamic.split('.')
splitted_path = path.split('.')
for idx, s in enumerate(splitted_dynamic):
if '{{ suffix }}' in s:
break
suffix_path = '.'.join(splitted_path[idx + 1:])
if suffix_path:
suffix_path = "." + suffix_path
suffix = splitted_path[idx] + suffix_path
dynamic_path = dynamic
dynamic_variable_path = dynamic + suffix_path
break
elif path.startswith(dynamic):
subpaths = path[len(dynamic) :].split(".", 1) subpaths = path[len(dynamic) :].split(".", 1)
if ( if (
subpaths[0] subpaths[0]
@ -136,20 +153,21 @@ class Paths:
suffix = ( suffix = (
dynamic.rsplit(".", 1)[-1] + subpaths[0] + "." + subpaths[1] dynamic.rsplit(".", 1)[-1] + subpaths[0] + "." + subpaths[1]
) )
dynamic_path = dynamic + "." + subpaths[1] dynamic_path = dynamic
break dynamic_variable_path = dynamic + "." + subpaths[1]
if suffix:
break break
if suffix is None and not path in self._data: if suffix is None and not path in self._data:
return None, None return None, None, None
if suffix and dynamic_path: dynamic = None
path = dynamic_path if suffix and dynamic_variable_path:
return self._data[path], suffix path = dynamic_variable_path
dynamic = self._data[dynamic_path]
return self._data[path], suffix, dynamic
def __getitem__( def __getitem__(
self, self,
path: str, path: str,
) -> Union[Family, Variable]: ) -> Union[Family, _Variable]:
if not path in self._data: if not path in self._data:
raise AttributeError(f"cannot find variable or family {path}") raise AttributeError(f"cannot find variable or family {path}")
return self._data[path] return self._data[path]
@ -217,7 +235,6 @@ class ParserVariable:
# #
self.family = Family self.family = Family
self.dynamic = Dynamic self.dynamic = Dynamic
self.variable = Variable
self.choice = Choice self.choice = Choice
# #
self.exclude_imports = [] self.exclude_imports = []
@ -229,13 +246,23 @@ class ParserVariable:
self.is_init = False self.is_init = False
super().__init__() super().__init__()
def get_variable(self):
convert_options = list(CONVERT_OPTION)
convert_options.extend(self.rougailconfig["custom_types"])
class Variable(_Variable):
type: Literal[*convert_options] = convert_options[0]
return Variable
def init(self): def init(self):
if self.is_init: if self.is_init:
return return
self.variable = self.get_variable()
hint = get_type_hints(self.dynamic) hint = get_type_hints(self.dynamic)
self.family_types = hint["type"].__args__ # pylint: disable=W0201 self.family_types = hint["type"].__args__ # pylint: disable=W0201
self.family_attrs = frozenset( # pylint: disable=W0201 self.family_attrs = frozenset( # pylint: disable=W0201
set(hint) | {"redefine"} - {"name", "path", "xmlfiles"} set(hint) - {"name", "path", "xmlfiles"} | {"redefine"}
) )
self.family_calculations = self.search_calculation( # pylint: disable=W0201 self.family_calculations = self.search_calculation( # pylint: disable=W0201
hint hint
@ -246,7 +273,7 @@ class ParserVariable:
# #
hint = get_type_hints(self.choice) hint = get_type_hints(self.choice)
self.choice_attrs = frozenset( # pylint: disable=W0201 self.choice_attrs = frozenset( # pylint: disable=W0201
set(hint) | {"redefine", "exists"} - {"name", "path", "xmlfiles"} set(hint) - {"name", "path", "xmlfiles"} | {"redefine", "exists"}
) )
self.choice_calculations = self.search_calculation( # pylint: disable=W0201 self.choice_calculations = self.search_calculation( # pylint: disable=W0201
hint hint
@ -290,7 +317,7 @@ class ParserVariable:
if isinstance(value, dict) and not self.is_calculation( if isinstance(value, dict) and not self.is_calculation(
key, key,
value, value,
"variable", self.choice_calculations,
False, False,
): ):
break break
@ -356,8 +383,7 @@ class ParserVariable:
family_is_leadership: bool = False, family_is_leadership: bool = False,
family_is_dynamic: bool = False, family_is_dynamic: bool = False,
) -> None: ) -> None:
""" Parse a family """Parse a family"""
"""
if obj is None: if obj is None:
return return
family_obj = {} family_obj = {}
@ -371,10 +397,11 @@ class ParserVariable:
else: else:
subfamily_obj[key] = value subfamily_obj[key] = value
if path in self.paths: if path in self.paths:
# it's just for modify subfamily or subvariable, do not redefine
if family_obj: if family_obj:
if not obj.pop("redefine", False): if not obj.pop("redefine", False):
raise Exception( raise Exception(
"The family {path} already exists and she is not redefined" f"The family {path} already exists and she is not redefined in {filemane}"
) )
self.paths.add( self.paths.add(
path, path,
@ -429,8 +456,7 @@ class ParserVariable:
self, self,
obj: Dict[str, Any], obj: Dict[str, Any],
) -> Iterator[str]: ) -> Iterator[str]:
""" List attributes """List attributes"""
"""
force_to_variable = [] force_to_variable = []
for key, value in obj.items(): for key, value in obj.items():
if key in force_to_variable: if key in force_to_variable:
@ -446,7 +472,7 @@ class ParserVariable:
if isinstance(value, dict) and not self.is_calculation( if isinstance(value, dict) and not self.is_calculation(
key, key,
value, value,
"family", self.family_calculations,
False, False,
): ):
# it's a dict, so a new variables! # it's a dict, so a new variables!
@ -462,8 +488,7 @@ class ParserVariable:
filenames: Union[str, List[str]], filenames: Union[str, List[str]],
family_is_dynamic: bool, family_is_dynamic: bool,
) -> None: ) -> None:
""" Add a new family """Add a new family"""
"""
family["path"] = path family["path"] = path
if not isinstance(filenames, list): if not isinstance(filenames, list):
filenames = [filenames] filenames = [filenames]
@ -483,7 +508,7 @@ class ParserVariable:
if not self.is_calculation( if not self.is_calculation(
key, key,
value, value,
"family", self.family_calculations,
False, False,
): ):
continue continue
@ -528,8 +553,7 @@ class ParserVariable:
family_is_leadership: bool = False, family_is_leadership: bool = False,
family_is_dynamic: bool = False, family_is_dynamic: bool = False,
) -> None: ) -> None:
""" Parse variable """Parse variable"""
"""
if obj is None: if obj is None:
obj = {} obj = {}
extra_attrs = set(obj) - self.choice_attrs extra_attrs = set(obj) - self.choice_attrs
@ -545,7 +569,9 @@ class ParserVariable:
return return
if not obj.pop("redefine", False): if not obj.pop("redefine", False):
raise Exception(f'Variable "{path}" already exists') raise Exception(f'Variable "{path}" already exists')
self.paths.add(path, self.paths[path].model_copy(update=obj), False, force=True) self.paths.add(
path, self.paths[path].model_copy(update=obj), False, force=True
)
self.paths[path].xmlfiles.append(filename) self.paths[path].xmlfiles.append(filename)
else: else:
if "exists" in obj and obj.pop("exists"): if "exists" in obj and obj.pop("exists"):
@ -576,7 +602,7 @@ class ParserVariable:
if self.is_calculation( if self.is_calculation(
key, key,
value, value,
"variable", self.choice_calculations,
False, False,
): ):
try: try:
@ -597,7 +623,7 @@ class ParserVariable:
if not self.is_calculation( if not self.is_calculation(
key, key,
val, val,
"variable", self.choice_calculations,
True, True,
): ):
continue continue
@ -617,8 +643,7 @@ class ParserVariable:
) from err ) from err
def parse_params(self, path, obj): def parse_params(self, path, obj):
""" Parse variable params """Parse variable params"""
"""
if "params" not in obj: if "params" not in obj:
return return
if not isinstance(obj["params"], dict): if not isinstance(obj["params"], dict):
@ -628,7 +653,9 @@ class ParserVariable:
try: try:
params.append(AnyParam(key=key, value=val, type="any")) params.append(AnyParam(key=key, value=val, type="any"))
except ValidationError as err: except ValidationError as err:
raise Exception(f'"{key}" has an invalid "params" for {path}: {err}') from err raise Exception(
f'"{key}" has an invalid "params" for {path}: {err}'
) from err
obj["params"] = params obj["params"] = params
def add_variable( def add_variable(
@ -681,7 +708,7 @@ class ParserVariable:
############################################################################################### ###############################################################################################
def set_name( def set_name(
self, self,
obj: Union[Variable, Family], obj: Union[_Variable, Family],
option_prefix: str, option_prefix: str,
): ):
"""Set Tiramisu object name""" """Set Tiramisu object name"""
@ -697,14 +724,10 @@ class ParserVariable:
self, self,
attribute: str, attribute: str,
value: dict, value: dict,
typ: Literal["variable", "family"], calculations: list,
inside_list: bool, inside_list: bool,
): ):
"""Check if it's a calculation""" """Check if it's a calculation"""
if typ == "variable":
calculations = self.choice_calculations
else:
calculations = self.family_calculations
if inside_list: if inside_list:
calculations = calculations[0] calculations = calculations[0]
else: else:
@ -786,7 +809,9 @@ class RougailConvert(ParserVariable):
inside_list = [] inside_list = []
outside_list = [] outside_list = []
for key, value in hint.items(): for key, value in hint.items():
if "Union" in value.__class__.__name__ and Calculation in value.__args__: if "Union" in value.__class__.__name__ and (
Calculation in value.__args__ or VariableCalculation in value.__args__
):
outside_list.append(key) outside_list.append(key)
if ( if (
"Union" in value.__class__.__name__ "Union" in value.__class__.__name__
@ -914,7 +939,9 @@ class RougailConvert(ParserVariable):
f"pffff version ... {version} not in {self.supported_version}" f"pffff version ... {version} not in {self.supported_version}"
) )
def annotate(self): def annotate(
self,
):
"""Apply annotation""" """Apply annotation"""
if not self.paths.has_value(): if not self.paths.has_value():
self.parse_directories() self.parse_directories()
@ -940,7 +967,7 @@ class RougailConvert(ParserVariable):
def save( def save(
self, self,
filename: None, filename: str,
): ):
"""Return tiramisu object declaration as a string""" """Return tiramisu object declaration as a string"""
self.annotate() self.annotate()

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -8,7 +8,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -1,7 +1,7 @@
"""Rougail object model """Rougail object model
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2023 Copyright (C) 2023-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -21,16 +21,77 @@ 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
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None] BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
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 Param(BaseModel): class Param(BaseModel):
key: str key: str
model_config = ConfigDict(extra="forbid")
class AnyParam(Param): class AnyParam(Param):
type: str type: str
@ -70,6 +131,9 @@ PARAM_TYPES = {
class Calculation(BaseModel): class Calculation(BaseModel):
path_prefix: Optional[str] path_prefix: Optional[str]
path: str path: str
inside_list: bool
model_config = ConfigDict(extra="forbid")
def get_realpath( def get_realpath(
self, self,
@ -85,7 +149,7 @@ class Calculation(BaseModel):
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_path = self.get_realpath(param["variable"])
variable, suffix = objectspace.paths.get_with_dynamic(variable_path) variable, suffix, dynamic = objectspace.paths.get_with_dynamic(variable_path)
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 {variable_path}")
@ -95,6 +159,7 @@ class Calculation(BaseModel):
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_path = self.get_realpath(param["variable"])
@ -114,7 +179,6 @@ class JinjaCalculation(Calculation):
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,
@ -147,20 +211,21 @@ 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 in get_jinja_variable_to_param( for sub_variable, suffix, true_path, dynamic in get_jinja_variable_to_param(
self.jinja, self.jinja,
objectspace, objectspace,
variable.xmlfiles, variable.xmlfiles,
objectspace.functions, objectspace.functions,
self.path_prefix, self.path_prefix,
): ):
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(
@ -223,14 +288,13 @@ class VariableCalculation(Calculation):
] ]
variable: StrictStr variable: StrictStr
propertyerror: bool = True propertyerror: bool = True
inside_list: bool
def to_function( def to_function(
self, self,
objectspace, objectspace,
) -> dict: ) -> dict:
variable_path = self.get_realpath(self.variable) variable_path = self.get_realpath(self.variable)
variable, suffix = objectspace.paths.get_with_dynamic(variable_path) variable, suffix, dynamic = objectspace.paths.get_with_dynamic(variable_path)
if not variable: if not variable:
raise Exception(f"pffff {variable_path}") raise Exception(f"pffff {variable_path}")
if not isinstance(variable, objectspace.variable): if not isinstance(variable, objectspace.variable):
@ -242,6 +306,7 @@ 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
@ -251,10 +316,6 @@ class VariableCalculation(Calculation):
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 (
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 self.inside_list and self.path in objectspace.multis:
if ( if (
not objectspace.paths.is_dynamic(variable_path) not objectspace.paths.is_dynamic(variable_path)
@ -277,7 +338,6 @@ class InformationCalculation(Calculation):
attribute_name: Literal["default"] attribute_name: Literal["default"]
information: StrictStr information: StrictStr
variable: Optional[StrictStr] variable: Optional[StrictStr]
inside_list: bool
def to_function( def to_function(
self, self,
@ -346,43 +406,15 @@ class Family(BaseModel):
xmlfiles: List[str] = [] xmlfiles: List[str] = []
path: 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
class Variable(BaseModel): class _Variable(BaseModel):
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
params: Optional[List[Param]] = None params: Optional[List[Param]] = None
@ -399,17 +431,19 @@ class Variable(BaseModel):
xmlfiles: List[str] = [] xmlfiles: List[str] = []
path: str path: str
class ConfigDict: model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
arbitrary_types_allowed = True
class Choice(Variable): class Choice(_Variable):
type: Literal["choice"] = "choice"
choices: Union[List[BASETYPE_CALC], Calculation] 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")

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license

View file

@ -10,7 +10,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -33,10 +33,9 @@ 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
@ -122,7 +121,7 @@ class TiramisuReflector:
[ [
"from jinja2 import StrictUndefined, DictLoader", "from jinja2 import StrictUndefined, DictLoader",
"from jinja2.sandbox import SandboxedEnvironment", "from jinja2.sandbox import SandboxedEnvironment",
"from rougail.annotator.variable import CONVERT_OPTION", "from rougail import CONVERT_OPTION",
"from tiramisu.error import ValueWarning", "from tiramisu.error import ValueWarning",
"def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):", "def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):",
" global ENV, CONVERT_OPTION", " global ENV, CONVERT_OPTION",
@ -393,6 +392,7 @@ class Common:
param["variable"], param["variable"],
param.get("propertyerror", True), param.get("propertyerror", True),
param.get("suffix"), param.get("suffix"),
param.get("dynamic"),
) )
if param["type"] == "any": if param["type"] == "any":
if isinstance(param["value"], str): if isinstance(param["value"], str):
@ -407,6 +407,7 @@ class Common:
param, param,
propertyerror, propertyerror,
suffix: Optional[str], suffix: Optional[str],
dynamic,
) -> str: ) -> str:
"""build variable parameters""" """build variable parameters"""
if param.path == self.elt.path: if param.path == self.elt.path:
@ -417,7 +418,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[param.path.rsplit(".", 1)[0]].get( family = self.tiramisu.reflector_objects[dynamic.path].get(
self.calls, self.elt.path self.calls, self.elt.path
) )
params.extend([f"'{suffix}'", f"{family}"]) params.extend([f"'{suffix}'", f"{family}"])
@ -469,6 +470,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(
@ -528,7 +532,7 @@ class Variable(Common):
else: else:
validators.append(val) validators.append(val)
keys["validators"] = "[" + ", ".join(validators) + "]" keys["validators"] = "[" + ", ".join(validators) + "]"
for key, value in CONVERT_OPTION[self.elt.type].get("initkwargs", {}).items(): for key, value in CONVERT_OPTION.get(self.elt.type, {}).get("initkwargs", {}).items():
if isinstance(value, str): if isinstance(value, str):
value = f"'{value}'" value = f"'{value}'"
keys[key] = value keys[key] = value

View file

@ -4,7 +4,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2021 Copyright (C) 2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -42,7 +42,7 @@ from .error import UpgradeError
from .utils import normalize_family from .utils import normalize_family
from .config import RougailConfig from .config import RougailConfig
from .annotator.variable import CONVERT_OPTION from .object_model import CONVERT_OPTION
VERSIONS = ["0.10", "1.0"] VERSIONS = ["0.10", "1.0"]

View file

@ -9,7 +9,7 @@ Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021 Copyright (C) 2019-2021
Silique (https://www.silique.fr) Silique (https://www.silique.fr)
Copyright (C) 2022-2023 Copyright (C) 2022-2024
distribued with GPL-2 or later license distribued with GPL-2 or later license
@ -112,8 +112,8 @@ 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 = objectspace.paths.get_with_dynamic( variable, suffix, dynamic = objectspace.paths.get_with_dynamic(
get_realpath(variable_path, path_prefix) get_realpath(variable_path, path_prefix)
) )
if variable and variable.path in objectspace.variables: if variable and variable.path in objectspace.variables:
yield variable, suffix, variable_path yield variable, suffix, variable_path, dynamic

7
tests/custom.py Normal file
View file

@ -0,0 +1,7 @@
from os import environ
environ['TIRAMISU_LOCALE'] = 'en'
from tiramisu import StrOption
class CustomOption(StrOption):
pass

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
["rougail.empty"]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,4 @@
---
version: '1.0'
custom:
type: custom

View file

@ -0,0 +1,6 @@
{
"rougail.custom": {
"owner": "default",
"value": null
}
}

View file

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

View file

@ -0,0 +1,6 @@
{
"rougail.custom": {
"owner": "default",
"value": null
}
}

View file

@ -0,0 +1,24 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
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 = CustomOption(name="custom", doc="custom", properties=frozenset({"basic", "mandatory"}))
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,28 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
ALLOWED_LEADER_PROPERTIES.add("basic")
ALLOWED_LEADER_PROPERTIES.add("standard")
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 = CustomOption(name="custom", doc="custom", properties=frozenset({"basic", "mandatory"}))
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"}))
option_6 = CustomOption(name="custom", doc="custom", properties=frozenset({"basic", "mandatory"}))
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"basic"}))
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
["rougail.general.adresse_ip_eth0", "rougail.general.adresse_netmask_eth0", "rougail.general.adresse_ip"]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
["rougail.general.adresse_ip_eth0", "rougail.general.adresse_ip"]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
["rougail.general.adresse_ip_eth0", "rougail.general.adresse_netmask_eth0"]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -21,7 +21,7 @@ def _load_functions(path):
_load_functions('tests/dictionaries/../eosfunc/test.py') _load_functions('tests/dictionaries/../eosfunc/test.py')
from jinja2 import StrictUndefined, DictLoader from jinja2 import StrictUndefined, DictLoader
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from rougail.annotator.variable import CONVERT_OPTION from rougail import CONVERT_OPTION
from tiramisu.error import ValueWarning from tiramisu.error import ValueWarning
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs): def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
global ENV, CONVERT_OPTION global ENV, CONVERT_OPTION

View file

@ -0,0 +1 @@
[]

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