WIP: Expand the developer documentation #27

Draft
gremond wants to merge 22 commits from develop into developer_docs
4094 changed files with 17737 additions and 26467 deletions

View file

@ -394,6 +394,24 @@ Copy a variable in another:
type: variable
variable: _.my_variable
Copy the default value from a variable, means copy type, params and multi attribute too if not define in second variable.
.. code-block:: yaml
---
version: 1.1
my_variable:
multi: true
type: domainname
params:
allow_ip: true
my_calculated_variable:
default:
type: variable
variable: _.var1
Here my_calculated_variable is a domainname variable with parameter allow_ip=True and multi to true.
Copy one variable to another if the source has no `property` problem:
.. code-block:: yaml

View file

@ -28,19 +28,27 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from tiramisu import Config
from copy import copy
from tiramisu.error import PropertiesOptionError
from warnings import warn
from typing import List
from .convert import RougailConvert
from .config import RougailConfig
from .update import RougailUpgrade
from .object_model import CONVERT_OPTION
from .utils import normalize_family
def tiramisu_display_name(kls) -> str:
def tiramisu_display_name(kls, subconfig) -> str:
"""Replace the Tiramisu display_name function to display path + description"""
doc = kls.impl_get_information("doc", None)
doc = kls._get_information(subconfig, "doc", None)
comment = f" ({doc})" if doc and doc != kls.impl_getname() else ""
return f"{kls.impl_getpath()}{comment}"
if "{{ suffix }}" in comment:
comment = comment.replace('{{ suffix }}', str(subconfig.suffixes[-1]))
path = kls.impl_getpath()
if "{{ suffix }}" in path:
path = path.replace('{{ suffix }}', normalize_family(str(subconfig.suffixes[-1])))
return f"{path}{comment}"
class Rougail:
@ -61,9 +69,10 @@ class Rougail:
path_prefix: str,
) -> None:
"""Add a prefix"""
self.converted.load_config()
self.converted.parse_directories(path_prefix)
def get_config(self):
def run(self):
"""Get Tiramisu Config"""
if not self.config:
tiram_obj = self.converted.save(self.rougailconfig["tiramisu_cache"])
@ -77,5 +86,47 @@ class Rougail:
self.config.property.read_write()
return self.config
def get_config(self):
warn("get_config is deprecated, use run instead", DeprecationWarning, stacklevel=2)
return self.run()
__ALL__ = ("Rougail", "RougailConfig", "RougailUpgrade")
def user_datas(self,
user_datas: List[dict]):
values = {}
errors = []
warnings = []
for datas in user_datas:
for name, data in datas.get('values', {}).items():
values.setdefault(name, {}).update(data)
errors.extend(datas.get('errors', []))
warnings.extend(datas.get('warnings', []))
while values:
value_is_set = False
for option in self.config:
if option.path() in values and option.index() in values[option.path()]:
try:
option.value.set(values[option.path()])
value_is_set = True
values.pop(option.path())
except:
pass
if not value_is_set:
break
for path, data in values.items():
for index, value in data.items():
try:
print('attention', path, value)
self.config.option(path).value.set(value)
print('pfff')
except AttributeError as err:
errors.append(str(err))
except ValueError as err:
errors.append(str(err).replace('"', "'"))
except PropertiesOptionError as err:
# warnings.append(f'"{err}" but is defined in "{self.filename}"')
warnings.append(str(err))
return {'errors': errors,
'warnings': warnings,
}
__all__ = ("Rougail", "RougailConfig", "RougailUpgrade")

View file

@ -45,7 +45,7 @@ def get_annotators(annotators, module_name):
path = str(pathobj)
if not path.endswith(".py") or path.endswith("__.py"):
continue
module = load_modules(path)
module = load_modules(module_name, path)
if "Annotator" not in dir(module):
continue
annotators[module_name].append(module.Annotator)
@ -62,21 +62,26 @@ class SpaceAnnotator: # pylint: disable=R0903
if ANNOTATORS is None:
ANNOTATORS = {}
get_annotators(ANNOTATORS, "rougail.annotator")
for extra_annotator in objectspace.rougailconfig["extra_annotators"]:
for extra_annotator in objectspace.extra_annotators:
if extra_annotator in ANNOTATORS:
continue
get_annotators(ANNOTATORS, extra_annotator)
for plugin in objectspace.plugins:
try:
get_annotators(ANNOTATORS, f'rougail.{plugin}.annotator')
except ModuleNotFoundError:
pass
annotators = ANNOTATORS["rougail.annotator"].copy()
for extra_annotator in objectspace.rougailconfig["extra_annotators"]:
for extra_annotator in objectspace.extra_annotators:
annotators.extend(ANNOTATORS[extra_annotator])
for plugin in objectspace.plugins:
annotators.extend(ANNOTATORS[f'rougail.{plugin}.annotator'])
annotators = sorted(annotators, key=get_level)
functions = {}
functions_files = objectspace.rougailconfig["functions_file"]
if not isinstance(functions_files, list):
functions_files = [functions_files]
functions_files = objectspace.functions_files
for functions_file in functions_files:
if isfile(functions_file):
loaded_modules = load_modules(functions_file)
loaded_modules = load_modules('function_file', functions_file)
for function in dir(loaded_modules):
if function.startswith("_"):
continue

View file

@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from typing import Optional
from rougail.i18n import _
from rougail.error import DictConsistencyError
from rougail.utils import get_realpath
from rougail.annotator.variable import Walk
from rougail.object_model import VariableCalculation
@ -65,13 +64,16 @@ class Annotator(Walk):
self.objectspace = objectspace
if not self.objectspace.paths:
return
self.modes = {
name: Mode(idx)
for idx, name in enumerate(self.objectspace.rougailconfig["modes_level"])
}
self.check_leadership()
self.remove_empty_families()
self.family_names()
if self.objectspace.modes_level:
self.modes = {
name: Mode(idx)
for idx, name in enumerate(self.objectspace.modes_level)
}
self.default_variable_mode = self.objectspace.default_variable_mode
self.default_family_mode = self.objectspace.default_family_mode
self.change_modes()
self.convert_help()
@ -93,7 +95,7 @@ class Annotator(Walk):
if isinstance(family, self.objectspace.family) and not self._has_variable(
family.path
):
if "." in family.path:
if self.objectspace.paths.default_namespace is None or "." in family.path:
removed_families.append(family.path)
removed_families.reverse()
for family in removed_families:
@ -119,15 +121,15 @@ class Annotator(Walk):
def change_modes(self):
"""change the mode of variables"""
modes_level = self.objectspace.rougailconfig["modes_level"]
default_variable_mode = self.objectspace.rougailconfig["default_variable_mode"]
modes_level = self.objectspace.modes_level
default_variable_mode = self.default_variable_mode
if default_variable_mode not in modes_level:
msg = _(
f'default variable mode "{default_variable_mode}" is not a valid mode, '
f"valid modes are {modes_level}"
)
raise DictConsistencyError(msg, 72, None)
default_family_mode = self.objectspace.rougailconfig["default_family_mode"]
default_family_mode = self.default_family_mode
if default_family_mode not in modes_level:
msg = _(
f'default family mode "{default_family_mode}" is not a valid mode, '
@ -141,12 +143,21 @@ class Annotator(Walk):
families.reverse()
for family in families:
self._change_family_mode(family)
if self.objectspace.paths.default_namespace is None:
for variable_path in self.objectspace.parents['.']:
variable = self.objectspace.paths[variable_path]
if variable.type == "symlink" or variable_path in self.objectspace.families:
continue
self._set_default_mode_variable(variable,
self.default_variable_mode,
check_level=False,
)
def valid_mode(
self,
obj,
) -> None:
modes_level = self.objectspace.rougailconfig["modes_level"]
modes_level = self.objectspace.modes_level
if self._has_mode(obj) and obj.mode not in modes_level:
msg = _(
f'mode "{obj.mode}" for "{obj.name}" is not a valid mode, '
@ -194,11 +205,12 @@ class Annotator(Walk):
self,
variable: "self.objectspace.variable",
family_mode: Optional[str],
check_level: bool=True,
) -> None:
# auto_save variable is set to 'basic' mode
# if its mode is not defined by the user
if not self._has_mode(variable) and variable.auto_save is True:
variable.mode = self.objectspace.rougailconfig["modes_level"][0]
variable.mode = self.objectspace.modes_level[0]
# mandatory variable without value is a basic variable
elif (
not self._has_mode(variable)
@ -206,8 +218,8 @@ class Annotator(Walk):
and variable.default is None
and variable.path not in self.objectspace.default_multi
):
variable_mode = self.objectspace.rougailconfig["modes_level"][0]
if family_mode and self.modes[variable_mode] < self.modes[family_mode]:
variable_mode = self.objectspace.modes_level[0]
if check_level and family_mode and self.modes[variable_mode] < self.modes[family_mode]:
msg = _(
f'the variable "{variable.name}" is mandatory so in "{variable_mode}" mode '
f'but family has the higher family mode "{family_mode}"'
@ -231,20 +243,17 @@ class Annotator(Walk):
leader: "self.objectspace.variable",
follower: "self.objectspace.variable",
) -> None:
if follower.auto_save is True:
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
raise DictConsistencyError(msg, 29, follower.xmlfiles)
if leader == follower:
# it's a leader
if not leader.mode:
self._set_auto_mode(
leader, self.objectspace.rougailconfig["default_variable_mode"]
leader, self.default_variable_mode
)
return
if self._has_mode(follower):
follower_mode = follower.mode
else:
follower_mode = self.objectspace.rougailconfig["default_variable_mode"]
follower_mode = self.default_variable_mode
if self.modes[leader.mode] > self.modes[follower_mode]:
if self._has_mode(follower) and not self._has_mode(leader):
# if follower has mode but not the leader
@ -266,8 +275,8 @@ class Annotator(Walk):
if family.mode:
family_mode = family.mode
else:
family_mode = self.objectspace.rougailconfig["default_family_mode"]
min_variable_mode = self.objectspace.rougailconfig["modes_level"][-1]
family_mode = self.default_family_mode
min_variable_mode = self.objectspace.modes_level[-1]
# change variable mode, but not if variables are not in a family
is_leadership = family.type == "leadership"
if family.path in self.objectspace.parents:
@ -277,9 +286,7 @@ class Annotator(Walk):
continue
if variable_path in self.objectspace.families:
if not variable.mode:
variable.mode = self.objectspace.rougailconfig[
"default_family_mode"
]
variable.mode = self.default_family_mode
else:
self._change_variable_mode(variable, family_mode, is_leadership)
if self.modes[min_variable_mode] > self.modes[variable.mode]:
@ -301,7 +308,7 @@ class Annotator(Walk):
if variable.mode:
variable_mode = variable.mode
else:
variable_mode = self.objectspace.rougailconfig["default_variable_mode"]
variable_mode = self.default_variable_mode
# none basic variable in high level family has to be in high level
if not is_follower and self.modes[variable_mode] < self.modes[family_mode]:
if self._has_mode(variable):
@ -314,21 +321,6 @@ class Annotator(Walk):
if not variable.mode:
variable.mode = variable_mode
def dynamic_families(self):
"""link dynamic families to object"""
for family in self.get_families():
if family.type != "dynamic":
continue
for variable in self.objectspace.parents[family.path]:
if (
isinstance(variable, self.objectspace.family)
and not variable.leadership
):
msg = _(
f'dynamic family "{family.name}" cannot contains another family'
)
raise DictConsistencyError(msg, 22, family.xmlfiles)
def convert_help(self):
"""Convert variable help"""
for family in self.get_families():

View file

@ -112,37 +112,37 @@ class Annotator(Walk):
self._convert_property(variable)
if variable.hidden:
if variable.hidden is True:
self.frozen[variable.path] = True
elif self.frozen.get(variable.path) is not True:
self.frozen.setdefault(variable.path, []).append(variable.hidden)
if variable.path in self.frozen:
frozen = self.frozen[variable.path]
self.frozen[path] = True
elif self.frozen.get(path) is not True:
self.frozen.setdefault(path, []).append(variable.hidden)
if path in self.frozen:
frozen = self.frozen[path]
if frozen is True:
value = True
else:
value = []
for calculation in frozen:
calculation_object = calculation.__class__
calculation_dict = calculation.model_dump().copy()
calculation_dict["attribute_name"] = "frozen"
calculation_dict["path"] = variable.path
value.append(calculation_object(**calculation_dict))
calculation_copy = calculation.copy()
calculation_copy.attribute_name = 'frozen'
calculation_copy.ori_path = calculation_copy.path
calculation_copy.path = path
value.append(calculation_copy)
if len(value) == 1:
value = value[0]
self.objectspace.properties.add(path, "frozen", value)
if not variable.auto_save:
# if auto_save, save calculated value
self.objectspace.properties.add(path, "force_default_on_freeze", True)
if variable.mandatory and variable.multi:
if not variable.empty and self.objectspace.multis.get(variable.path, False):
# a multi could not have "None" has value
# to permit it, just add mandatory="False"
# to permit it, just add empty="false"
self.objectspace.properties.add(path, "notempty", True)
if variable.unique:
self.objectspace.properties.add(path, "unique", True)
if variable.unique is False:
self.objectspace.properties.add(path, "notunique", True)
if variable.auto_save:
self.objectspace.properties.add(variable.path, "force_store_value", True)
self.objectspace.properties.add(path, "force_store_value", True)
def _convert_property(
self,

View file

@ -55,6 +55,8 @@ class Annotator(Walk): # pylint: disable=R0903
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.version != '1.0' and variable.type == 'port':
self._convert_port(variable)
self._convert_value(variable)
def _convert_value(
@ -98,6 +100,14 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace.default_multi[variable.path] = variable.default
variable.default = None
def _convert_port(self, variable) -> None:
if variable.multi is False and isinstance(variable.default, int):
variable.default = str(variable.default)
elif variable.multi is True and isinstance(variable.default, list):
for idx, value in enumerate(variable.default):
if isinstance(value, int):
variable.default[idx] = str(value)
def valid_choices(self) -> None:
"""A variable with type "Choice" that is not mandatory must has "nil" value"""
for variable in self.get_variables():
@ -108,7 +118,7 @@ class Annotator(Walk): # pylint: disable=R0903
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:
if not variable.mandatory and not variable.multi:
self.add_choice_nil(variable)
def add_choice_nil(self, variable) -> None:

View file

@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from rougail.i18n import _
from rougail.error import DictConsistencyError
from rougail.object_model import Calculation
from rougail.object_model import Calculation, VariableCalculation
from tiramisu.error import display_list
@ -65,30 +65,41 @@ class Annotator(Walk): # pylint: disable=R0903
if not objectspace.paths:
return
self.objectspace = objectspace
if self.objectspace.main_namespace:
self.forbidden_name = [
"services",
self.objectspace.rougailconfig["variable_namespace"],
self.objectspace.main_namespace
]
for extra in self.objectspace.rougailconfig["extra_dictionaries"]:
for extra in self.objectspace.extra_dictionaries:
self.forbidden_name.append(extra)
else:
self.forbidden_name = []
# 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_test()
self.convert_examples()
self.convert_help()
self.verify_choices()
def convert_variable(self):
"""convert variable"""
for variable in self.get_variables():
if variable.version != "1.0":
if variable.type == "symlink":
continue
self._convert_variable_inference(variable)
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.version != "1.0":
self._convert_variable_inference(variable)
self._default_variable_copy_informations(variable)
if variable.multi is None:
variable.multi = False
if variable.type is None:
variable.type = "string"
self.objectspace.informations.add(
variable.path, "type", variable.type
)
self._convert_variable(variable)
def _convert_variable_inference(
@ -100,6 +111,8 @@ class Annotator(Walk): # pylint: disable=R0903
# choice type inference from the `choices` attribute
if variable.choices is not None:
variable.type = "choice"
elif variable.regexp is not None:
variable.type = "regexp"
elif variable.default not in [None, []]:
if isinstance(variable.default, list):
tested_value = variable.default[0]
@ -107,12 +120,37 @@ class Annotator(Walk): # pylint: disable=R0903
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.multi is None and not (variable.type is None and isinstance(variable.default, VariableCalculation)):
if variable.path in self.objectspace.leaders:
variable.multi = True
else:
variable.multi = isinstance(variable.default, list)
def _default_variable_copy_informations(
self,
variable,
) -> None:
# if a variable has a variable as default value, that means the type/params or multi should has same value
if variable.type is not None or not isinstance(variable.default, VariableCalculation):
return
# copy type and params
calculated_variable_path = variable.default.variable
calculated_variable, suffix = self.objectspace.paths.get_with_dynamic(
calculated_variable_path, variable.default.path_prefix, variable.path, variable.version, variable.namespace, variable.xmlfiles
)
if calculated_variable is None:
return
variable.type = calculated_variable.type
if variable.params is None and calculated_variable.params is not None:
variable.params = calculated_variable.params
# copy multi attribut
if variable.multi is None:
calculated_path = calculated_variable.path
if calculated_path in self.objectspace.leaders and variable.path in self.objectspace.followers and calculated_path.rsplit('.')[0] == variable.path.rsplit('.')[0]:
variable.multi = False
else:
variable.multi = calculated_variable.multi
def _convert_variable(
self,
variable: dict,
@ -129,8 +167,7 @@ class Annotator(Walk): # pylint: disable=R0903
self.objectspace.multis[variable.path] = True
if variable.path in self.objectspace.leaders:
if not self.objectspace.multis.get(variable.path, False):
msg = _(f'the variable "{variable.path}" in a leadership must be multi')
raise DictConsistencyError(msg, 32, variable.xmlfiles)
variable.multi = self.objectspace.multis[variable.path] = True
family = self.objectspace.paths[variable.path.rsplit(".", 1)[0]]
if variable.hidden:
family.hidden = variable.hidden
@ -138,19 +175,34 @@ class Annotator(Walk): # pylint: disable=R0903
variable.hidden = family.hidden
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')
msg = _(f'the variable "{variable.path}" has choices attribut but has not the "choice" type')
raise DictConsistencyError(msg, 11, variable.xmlfiles)
if variable.regexp is not None and variable.type != 'regexp':
msg = _(f'the variable "{variable.path}" has regexp attribut but has not the "regexp" type')
raise DictConsistencyError(msg, 37, variable.xmlfiles)
def convert_test(self):
"""Convert variable tests value"""
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.test is None:
# with we want remove test, we set "" has test value
continue
self.objectspace.informations.add(
variable.path, "test", tuple(variable.test)
)
def convert_examples(self):
"""Convert variable tests value"""
for variable in self.get_variables():
if variable.type == "symlink":
continue
if variable.examples is None:
continue
self.objectspace.informations.add(
variable.path, "examples", tuple(variable.examples)
)
def convert_help(self):
"""Convert variable help"""
for variable in self.get_variables():
@ -161,7 +213,7 @@ class Annotator(Walk): # pylint: disable=R0903
def verify_choices(self):
for variable in self.get_variables():
if variable.default is None or variable.type != 'choice':
if variable.type != 'choice' or variable.default is None:
continue
if not isinstance(variable.choices, list):
continue

View file

@ -28,52 +28,427 @@ 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 os.path import abspath, dirname, join
ROUGAILROOT = "/srv/rougail"
DTDDIR = join(dirname(abspath(__file__)), "data")
from pathlib import Path
from tiramisu import Config
from ruamel.yaml import YAML
from .utils import _, load_modules, normalize_family
from .convert import RougailConvert
RougailConfig = {
"default_dictionary_format_version": None,
"dictionaries_dir": [join(ROUGAILROOT, "dictionaries")],
"extra_dictionaries": {},
"services_dir": [join(ROUGAILROOT, "services")],
"patches_dir": join(ROUGAILROOT, "patches"),
"templates_dir": join(ROUGAILROOT, "templates"),
"destinations_dir": join(ROUGAILROOT, "destinations"),
"tmp_dir": join(ROUGAILROOT, "tmp"),
"functions_file": join(ROUGAILROOT, "functions.py"),
"system_service_directory": "/usr/lib/systemd/system",
"systemd_service_destination_directory": "/usr/local/lib",
"systemd_service_directory": "/systemd",
"systemd_service_file": "rougail.conf",
"systemd_service_ip_file": "rougail_ip.conf",
"systemd_tmpfile_factory_dir": "/usr/local/lib",
"systemd_tmpfile_directory": "/tmpfiles.d",
"systemd_tmpfile_file": "0rougail.conf",
"systemd_tmpfile_delete_before_create": False,
"variable_namespace": "rougail",
"variable_namespace_description": "Rougail",
"auto_freeze_variable": "server_deployed",
"internal_functions": [],
"multi_functions": [],
"extra_annotators": [],
"modes_level": ["basic", "standard", "advanced"],
"default_family_mode": "basic",
"default_variable_mode": "standard",
"default_files_engine": "jinja",
"default_files_mode": 644,
"default_files_owner": "root",
"default_files_group": "root",
"default_files_included": "no",
"default_overrides_engine": "jinja",
"default_service_names_engine": "none",
"default_certificate_domain": "rougail.server_name",
"base_option_name": "baseoption",
"export_with_import": True,
"force_convert_dyn_option_description": False,
"suffix": "",
"tiramisu_cache": None,
"custom_types": {},
}
RENAMED = {'dictionaries_dir': 'main_dictionaries',
'variable_namespace': 'main_namespace',
'functions_file': 'functions_files',
}
NOT_IN_TIRAMISU = {'custom_types': {},
}
SUBMODULES = None
def get_sub_modules():
global SUBMODULES
if SUBMODULES is None:
SUBMODULES = {}
for submodule in Path(__file__).parent.iterdir():
if submodule.name.startswith('_') or not submodule.is_dir():
continue
config_file = submodule / 'config.py'
if config_file.is_file():
SUBMODULES[submodule.name] = load_modules('rougail.' + submodule.name + '.config', str(config_file))
return SUBMODULES
def get_level(module):
return module['level']
class _RougailConfig:
def __init__(self,
backward_compatibility: bool,
root,
extra_vars: dict
):
self.backward_compatibility = backward_compatibility
self.root = root
self.config = Config(
self.root,
)
self.config.property.read_only()
self.extra_vars = extra_vars
self.not_in_tiramisu = NOT_IN_TIRAMISU | extra_vars
for variable, default_value in self.not_in_tiramisu.items():
if not isinstance(default_value, str):
default_value = default_value.copy()
setattr(self, variable, default_value)
def copy(self):
rougailconfig = _RougailConfig(self.backward_compatibility, self.root, self.extra_vars)
rougailconfig.config.value.importation(self.config.value.exportation())
rougailconfig.config.property.importation(self.config.property.exportation())
rougailconfig.config.property.read_only()
for variable in self.not_in_tiramisu:
value = getattr(self, variable)
if not isinstance(value, str):
value = value.copy()
setattr(rougailconfig, variable, value)
return rougailconfig
def __setitem__(self,
key,
value,
) -> None:
if key in self.not_in_tiramisu:
setattr(self, key, value)
else:
self.config.property.read_write()
if key == 'export_with_import':
key = 'not_export_with_import'
key = RENAMED.get(key, key)
option = self.config.option(key)
if option.isoptiondescription() and option.isleadership():
leader = list(value)
option.leader().value.reset()
option.leader().value.set(leader)
follower = option.followers()[0]
for idx, val in enumerate(value.values()):
self.config.option(follower.path(), idx).value.set(val)
elif key == 'not_export_with_import':
option.value.set(not value)
else:
option.value.set(value)
self.config.property.read_only()
def __getitem__(self,
key,
) -> None:
if key in self.not_in_tiramisu:
return getattr(self, key)
if key == 'export_with_import':
key = 'not_export_with_import'
option = self.config.option(key)
if option.isoptiondescription() and option.isleadership():
return self.get_leadership(option)
ret = self.config.option(key).value.get()
if key == 'not_export_with_import':
return not ret
return ret
def get_leadership(self,
option
) -> dict:
leader = None
followers = []
for opt, value in option.value.get().items():
if opt.issymlinkoption():
continue
if leader is None:
leader = value
else:
followers.append(value)
return dict(zip(leader, followers))
def parse(self, config) -> str:
for option in config:
if option.isoptiondescription():
yield from self.parse(option)
elif not option.issymlinkoption():
yield f'{option.path()}: {option.value.get()}'
def __repr__(self):
self.config.property.read_write()
try:
values = "\n".join(self.parse(self.config))
except Exception as err:
values = str(err)
self.config.property.read_only()
return values
class FakeRougailConvert(RougailConvert):
def __init__(self,
add_extra_options: bool,
) -> None:
self.add_extra_options = add_extra_options
super().__init__({})
def load_config(self) -> None:
self.sort_dictionaries_all = False
self.main_namespace = None
self.suffix = ''
self.custom_types = {}
self.functions_files = []
self.modes_level = []
self.extra_annotators = []
self.base_option_name = "baseoption"
self.export_with_import = True
self.internal_functions = []
self.plugins = ['structural_commandline']
self.add_extra_options = self.add_extra_options
def get_rougail_config(*,
backward_compatibility: bool=True,
add_extra_options: bool=True,
) -> _RougailConfig:
if backward_compatibility:
main_namespace_default = 'rougail'
else:
main_namespace_default = 'null'
rougail_options = """default_dictionary_format_version:
description: Dictionary format version by default, if not specified in dictionary file
alternative_name: v
choices:
- '1.0'
- '1.1'
mandatory: false
main_dictionaries:
description: 'Directories where dictionary files are placed'
type: unix_filename
alternative_name: m
params:
allow_relative: True
test_existence: True
types:
- directory
multi: true
sort_dictionaries_all:
description: Sort dictionaries from differents directories
negative_description: Sort dictionaries directory by directory
default: false
main_namespace:
description: Main namespace name
default: MAIN_MAMESPACE_DEFAULT
alternative_name: s
mandatory: false
extra_dictionaries:
description: Extra namespaces
type: leadership
disabled:
variable: main_namespace
when: null
names:
description: 'Extra namespace name'
alternative_name: xn
multi: true
mandatory: false
directories:
description: Directories where extra dictionary files are placed
alternative_name: xd
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- directory
multi: true
upgrade:
description: Update dictionaries to newest Rougail format version
negative_description: Do not update dictionaries to newest Rougail format version
default: false
upgrade_options:
description: Update informations
disabled:
variable: upgrade
when: false
main_dictionaries:
description: 'Directories where dictionary files will be placed'
default:
variable: __.main_dictionaries
extra_dictionary:
description: 'Directories where extra files will be placed'
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- directory
disabled:
variable: __.main_namespace
when: null
functions_files:
description: File with functions
alternative_name: c
type: unix_filename
params:
allow_relative: true
test_existence: true
types:
- file
multi: true
mandatory: false
modes_level:
description: All modes level available
default:
- basic
- standard
- advanced
commandline: false
default_family_mode:
description: Default mode for a family
default:
type: jinja
jinja: |
{{ modes_level[0] }}
validators:
- type: jinja
jinja: |
{% if default_family_mode not in modes_level %}
not in modes_level ({modes_level})
{% endif %}
commandline: false
default_variable_mode:
description: Default mode for a variable
default:
type: jinja
jinja: |
{{ modes_level[1] }}
validators:
- type: jinja
jinja: |
{% if default_variable_mode not in modes_level %}
not in modes_level ({modes_level})
{% endif %}
commandline: false
base_option_name:
description: Option name for the base option
default: baseoption
commandline: false
not_export_with_import:
description: In cache file, do not importation of Tiramisu and other dependencies
default: false
commandline: false
tiramisu_cache:
description: Tiramisu cache filename
alternative_name: t
type: unix_filename
mandatory: false
params:
allow_relative: true
internal_functions:
description: Name of internal functions that we can use as a function
multi: true
mandatory: false
commandline: false
extra_annotators:
description: Name of extra annotators
multi: true
mandatory: false
commandline: false
plugins:
description: Name of Rougail plugins
multi: true
mandatory: false
commandline: false
suffix:
description: Suffix add to generated option name
default: ''
mandatory: false
commandline: false
""".replace('MAIN_MAMESPACE_DEFAULT', main_namespace_default)
processes = {'structural': [],
'output': [],
'user data': [],
}
for module in get_sub_modules().values():
data = module.get_rougail_config()
processes[data['process']].append(data)
# reorder
for process in processes:
processes[process] = list(sorted(processes[process], key=get_level))
rougail_process = """step: # Load and exporter steps
disabled:
variable: upgrade"""
for process in processes:
if processes[process]:
objects = processes[process]
rougail_process += """
{NAME}:
description: Select for {NAME}
alternative_name: {NAME[0]}
choices:
""".format(NAME=normalize_family(process),
)
for obj in objects:
rougail_process += f" - {obj['name']}\n"
if process == 'structural':
rougail_process += " commandline: false"
elif process == 'user data':
rougail_process += """ multi: true
mandatory: false
"""
hidden_outputs = [process['name'] for process in processes['output'] if not process.get('allow_user_data', True)]
if hidden_outputs:
rougail_process += """ hidden:
type: jinja
jinja: |
"""
for hidden_output in hidden_outputs:
rougail_process += """ {% if _.output == 'NAME' %}
Cannot load user data for NAME output
{% endif %}
""".replace('NAME', hidden_output)
else:
rougail_process += ' default: {DEFAULT}'.format(DEFAULT=objects[0]['name'])
else:
rougail_process += """
{NAME}:
description: Select for {NAME}
hidden: true
mandatory: false
multi: true
""".format(NAME=normalize_family(process),
)
rougail_options += rougail_process
convert = FakeRougailConvert(add_extra_options)
convert._init()
convert.namespace = None
convert.parse_root_file(
'rougail.config',
'',
'1.1',
YAML().load(rougail_options),
)
extra_vars = {}
for process in processes:
for obj in processes[process]:
if 'extra_vars' in obj:
extra_vars |= obj['extra_vars']
if not 'options' in obj:
continue
convert.parse_root_file(
f'rougail.config.{obj["name"]}',
'',
'1.1',
YAML().load(obj['options']),
)
tiram_obj = convert.save(None)
optiondescription = {}
exec(tiram_obj, {}, optiondescription) # pylint: disable=W0122
return _RougailConfig(backward_compatibility,
optiondescription["option_0"],
extra_vars=extra_vars,
)
RougailConfig = get_rougail_config()

View file

@ -47,6 +47,10 @@ from typing import (
from pydantic import ValidationError
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
from pydantic import ValidationError
from warnings import warn
from tiramisu.error import display_list
from .annotator import SpaceAnnotator
@ -54,7 +58,9 @@ from .error import DictConsistencyError
from .i18n import _
from .object_model import CONVERT_OPTION # Choice,
from .object_model import (
PROPERTY_ATTRIBUTE,
CALCULATION_TYPES,
CALCULATION_PROPERTY_TYPES,
PARAM_TYPES,
AnyParam,
Calculation,
@ -65,7 +71,8 @@ from .object_model import (
VariableCalculation,
)
from .tiramisureflector import TiramisuReflector
from .utils import get_realpath
from .utils import get_realpath, normalize_family, load_modules
from .error import DictConsistencyError
property_types = Union[Literal[True], Calculation]
properties_types = Dict[str, property_types]
@ -97,7 +104,7 @@ class Property:
class Paths:
_regexp_relative = compile(r"^_*\.(.*)$")
regexp_relative = compile(r"^_*\.(.*)$")
def __init__(
self,
@ -105,6 +112,8 @@ class Paths:
) -> None:
self._data: Dict[str, Union[Variable, Family]] = {}
self._dynamics: Dict[str:str] = {}
if default_namespace is not None:
default_namespace = normalize_family(default_namespace)
self.default_namespace = default_namespace
self.path_prefix = None
@ -124,6 +133,18 @@ class Paths:
if not force and is_dynamic:
self._dynamics[path] = dynamic
def get_relative_path(self,
path: str,
current_path: str,
):
relative, subpath = path.split(".", 1)
relative_len = len(relative)
path_len = current_path.count(".")
if path_len + 1 == relative_len:
return subpath
parent_path = current_path.rsplit(".", relative_len)[0]
return parent_path + "." + subpath
def get_with_dynamic(
self,
path: str,
@ -134,12 +155,10 @@ class Paths:
xmlfiles: List[str],
) -> Any:
suffix = None
if version != "1.0" and self._regexp_relative.search(path):
relative, subpath = path.split(".", 1)
relative_len = len(relative)
path_len = current_path.count(".")
parent_path = current_path.rsplit(".", relative_len)[0]
path = parent_path + "." + subpath
if version != '1.0' and self.regexp_relative.search(path):
path = self.get_relative_path(path,
current_path,
)
else:
path = get_realpath(path, suffix_path)
dynamic = None
@ -159,7 +178,11 @@ class Paths:
new_path = name
continue
for dynamic_path in self._dynamics:
if '.' in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
version == "1.0"
and parent_dynamic == parent_path
@ -192,7 +215,11 @@ class Paths:
new_path = name
continue
for dynamic_path in self._dynamics:
if '.' in dynamic_path:
parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
else:
parent_dynamic = None
name_dynamic = dynamic_path
if (
"{{ suffix }}" not in name_dynamic
or parent_path != parent_dynamic
@ -203,6 +230,9 @@ class Paths:
if len(finded) != 1 or not finded[0]:
continue
suffixes.append(finded[0])
if new_path is None:
new_path = name_dynamic
else:
new_path += "." + name_dynamic
break
else:
@ -274,7 +304,7 @@ class Informations:
if path not in self._data:
self._data[path] = {}
if key in self._data[path]:
raise Exception(f"already key {key} in {path}")
raise Exception(f'an information "{key}" is already present in "{path}"')
self._data[path][key] = data
def get(
@ -286,7 +316,9 @@ class Informations:
class ParserVariable:
def __init__(self, rougailconfig):
self.paths = Paths(rougailconfig["variable_namespace"])
self.load_config(rougailconfig)
self.rougailconfig = rougailconfig
self.paths = Paths(self.main_namespace)
self.families = []
self.variables = []
self.parents = {".": []}
@ -297,13 +329,9 @@ class ParserVariable:
self.multis = {}
self.default_multi = {}
self.jinja = {}
self.rougailconfig = rougailconfig
self.convert_options = list(CONVERT_OPTION)
self.convert_options.extend(self.rougailconfig["custom_types"])
#
self.family = Family
self.dynamic = Dynamic
self.choice = Variable # Choice
self.convert_options = list(CONVERT_OPTION)
self.convert_options.extend(self.custom_types)
#
self.exclude_imports = []
self.informations = Informations()
@ -314,13 +342,49 @@ class ParserVariable:
self.is_init = False
super().__init__()
def init(self):
def load_config(self,
rougailconfig: 'RougailConfig',
) -> None:
self.rougailconfig = rougailconfig
self.main_namespace = rougailconfig["main_namespace"]
self.main_dictionaries = rougailconfig["main_dictionaries"]
if self.main_namespace:
self.extra_dictionaries = rougailconfig["extra_dictionaries"]
self.suffix = rougailconfig["suffix"]
self.default_dictionary_format_version = rougailconfig["default_dictionary_format_version"]
self.custom_types = rougailconfig["custom_types"]
self.functions_files = rougailconfig["functions_files"]
self.modes_level = rougailconfig["modes_level"]
self.default_variable_mode = rougailconfig["default_variable_mode"]
self.default_family_mode = rougailconfig["default_family_mode"]
self.extra_annotators = rougailconfig["extra_annotators"]
self.base_option_name = rougailconfig["base_option_name"]
self.export_with_import = rougailconfig["export_with_import"]
self.internal_functions = rougailconfig["internal_functions"]
self.add_extra_options = rougailconfig["structural_commandline.add_extra_options"]
self.plugins = []
def _init(self):
if self.is_init:
return
self.variable = Variable
variable = Variable
family = Family
if self.plugins:
root = Path(__file__).parent
for plugin in self.plugins:
module_path = root / plugin / 'object_model.py'
if not module_path.is_file():
continue
module = load_modules(f'rougail.{plugin}.object_model', str(module_path))
if 'Variable' in module.__all__:
variable = type(variable.__name__ + '_' + plugin, (variable, module.Variable), {})
if 'Family' in module.__all__:
family = type(family.__name__ + '_' + plugin, (family, module.Family), {})
self.variable = variable
self.family = family
self.dynamic = type(Dynamic.__name__, (Dynamic, family), {})
hint = get_type_hints(self.dynamic)
# FIXME: only for format 1.0
hint["variable"] = str
self.family_types = hint["type"].__args__ # pylint: disable=W0201
self.family_attrs = frozenset( # pylint: disable=W0201
set(hint) - {"name", "path", "xmlfiles"} | {"redefine"}
@ -330,16 +394,14 @@ class ParserVariable:
)
#
hint = get_type_hints(self.variable)
self.variable_types = (
self.convert_options
) # hint["type"].__args__ # pylint: disable=W0201
#
hint = get_type_hints(self.choice)
self.choice_attrs = frozenset( # pylint: disable=W0201
self.variable_attrs = frozenset( # pylint: disable=W0201
set(hint) - {"name", "path", "xmlfiles"} | {"redefine", "exists"}
)
self.choice_calculations = self.search_calculation( # pylint: disable=W0201
self.variable_calculations = self.search_calculation( # pylint: disable=W0201
hint
)
self.is_init = True
@ -379,13 +441,13 @@ class ParserVariable:
# all attributes are in variable object
# and values in attributes are not dict is not Calculation
if isinstance(obj, dict):
extra_keys = set(obj) - self.choice_attrs
extra_keys = set(obj) - self.variable_attrs
if not extra_keys:
for key, value in obj.items():
if isinstance(value, dict) and not self.is_calculation(
key,
value,
self.choice_calculations,
self.variable_calculations,
False,
):
break
@ -432,6 +494,9 @@ class ParserVariable:
if name.startswith("_"):
msg = f'the variable or family name "{name}" is incorrect, it must not starts with "_" character'
raise DictConsistencyError(msg, 16, [filename])
if not subpath:
path = name
else:
path = f"{subpath}.{name}"
if version == "0.1" and not isinstance(obj, dict) and obj is not None:
msg = f'the variable "{path}" has a wrong type "{type(obj)}"'
@ -493,7 +558,17 @@ class ParserVariable:
if family_obj:
if not obj.pop("redefine", False):
raise Exception(
f"The family {path} already exists and she is not redefined in {filemane}"
f"The family {path} already exists and she is not redefined in {filename}"
)
# convert to Calculation objects
self.parse_parameters(
path,
obj,
filename,
family_is_dynamic,
False,
version,
typ="family",
)
self.paths.add(
path,
@ -515,17 +590,29 @@ class ParserVariable:
extra_attrs = set(family_obj) - self.family_attrs
if extra_attrs:
raise Exception(f"extra attrs ... {extra_attrs}")
if self.get_family_or_variable_type(family_obj) == "dynamic":
obj_type = self.get_family_or_variable_type(family_obj)
if obj_type is None:
# auto set type
if '_dynamic' in family_obj:
dynamic = family_obj['_dynamic']
elif 'dynamic' in family_obj:
dynamic = family_obj['dynamic']
else:
dynamic = None
if isinstance(dynamic, (list, dict)):
family_obj['type'] = obj_type = 'dynamic'
if obj_type == "dynamic":
family_is_dynamic = True
parent_dynamic = path
if version == "1.0" and "{{ suffix }}" not in name:
name += "{{ suffix }}"
path += "{{ suffix }}"
if "{{ suffix }}" not in name:
if '{{ suffix }}' not in name:
if "variable" in family_obj:
name += '{{ suffix }}'
path += '{{ suffix }}'
else:
msg = f'dynamic family name must have "{{{{ suffix }}}}" in his name for "{path}"'
raise DictConsistencyError(msg, 13, [filename])
if version != "1.0" and not family_obj and comment:
family_obj["description"] = comment
if version != '1.0' and not family_obj and comment:
family_obj['description'] = comment
self.add_family(
path,
name,
@ -580,8 +667,8 @@ class ParserVariable:
):
# it's a dict, so a new variables!
continue
# FIXME should be remove with 1.0 format
if key == "variable" and obj.get("type") != "dynamic":
# 'variable' for compatibility to format 1.0
if key == "variable" and obj.get("type") != "dynamic" and obj.get("_type") != "dynamic":
continue
if key in self.family_attrs:
yield key
@ -599,6 +686,7 @@ class ParserVariable:
"""Add a new family"""
family["path"] = path
family["namespace"] = self.namespace
family["version"] = version
family["xmlfiles"] = [filename]
obj_type = self.get_family_or_variable_type(family)
if obj_type == "dynamic":
@ -625,9 +713,17 @@ class ParserVariable:
del family["variable"]
# FIXME only for 1.0
if "variable" in family:
raise Exception(
f'dynamic family must not have "variable" attribute for "{family["path"]}" in {family["xmlfiles"]}'
)
family['dynamic'] = {'type': 'variable',
'variable': family['variable'],
'propertyerror': False,
'allow_none': True,
}
del family['variable']
if version != "1.0":
warning = f'"variable" attribute in dynamic family "{ path }" is depreciated in {filename}'
warn(warning)
if "variable" in family:
raise Exception(f'dynamic family must not have "variable" attribute for "{family["path"]}" in {family["xmlfiles"]}')
else:
family_obj = self.family
# convert to Calculation objects
@ -679,7 +775,7 @@ class ParserVariable:
if version == "1.0" or isinstance(obj, dict):
if obj is None:
obj = {}
extra_attrs = set(obj) - self.choice_attrs
extra_attrs = set(obj) - self.variable_attrs
else:
extra_attrs = []
obj = {"default": obj}
@ -720,7 +816,7 @@ class ParserVariable:
# so do nothing
return
if "redefine" in obj and obj["redefine"]:
msg = f'cannot redefine the inexisting variable "{path}" in {filename}'
msg = f'cannot redefine the inexisting variable "{path}"'
raise DictConsistencyError(msg, 46, [filename])
obj["path"] = path
self.add_variable(
@ -745,7 +841,7 @@ class ParserVariable:
):
"""Parse variable or family parameters"""
if typ == "variable":
calculations = self.choice_calculations
calculations = self.variable_calculations
else:
calculations = self.family_calculations
for key, value in obj.items():
@ -846,7 +942,7 @@ class ParserVariable:
variable_type = self.get_family_or_variable_type(variable)
obj = {
"symlink": SymLink,
"choice": self.choice,
"choice": self.variable,
}.get(variable_type, self.variable)
try:
variable_obj = obj(name=name, **variable)
@ -861,7 +957,11 @@ class ParserVariable:
parent_dynamic,
)
self.variables.append(variable["path"])
self.parents[variable["path"].rsplit(".", 1)[0]].append(variable["path"])
if '.' in variable["path"]:
parent_path = variable["path"].rsplit(".", 1)[0]
else:
parent_path = "."
self.parents[parent_path].append(variable["path"])
self.set_name(
variable_obj,
"option_",
@ -875,7 +975,10 @@ class ParserVariable:
del self.paths[path]
self.families.remove(path)
del self.parents[path]
if '.' in path:
parent = path.rsplit(".", 1)[0]
else:
parent = '.'
self.parents[parent].remove(path)
###############################################################################################
@ -890,7 +993,7 @@ class ParserVariable:
self.index += 1
self.reflector_names[
obj.path
] = f'{option_prefix}{self.index}{self.rougailconfig["suffix"]}'
] = f'{option_prefix}{self.index}{self.suffix}'
###############################################################################################
# calculations
@ -907,11 +1010,16 @@ class ParserVariable:
calculations = calculations[0]
else:
calculations = calculations[1]
return (
attribute in calculations
and isinstance(value, dict)
and value.get("type") in CALCULATION_TYPES
)
if not isinstance(value, dict) or attribute not in calculations:
return False
if 'type' in value:
return value['type'] in CALCULATION_TYPES
# auto set type
typ = set(CALCULATION_TYPES) & set(value)
if len(typ) == 1:
value['type'] = list(typ)[0]
return True
return False
def set_calculation(
self,
@ -944,6 +1052,11 @@ class ParserVariable:
raise Exception("params must be a dict")
params = []
for key, val in calculation_object["params"].items():
if isinstance(val, dict) and "type" not in val:
# auto set type
param_typ = set(CALCULATION_TYPES) & set(val)
if len(param_typ) == 1:
val['type'] = list(param_typ)[0]
if not isinstance(val, dict) or "type" not in val:
param_typ = "any"
val = {
@ -976,6 +1089,9 @@ class ParserVariable:
if typ == "suffix" and not family_is_dynamic:
msg = f'suffix calculation for "{attribute}" in "{path}" cannot be set none dynamic family'
raise DictConsistencyError(msg, 53, xmlfiles)
if attribute in PROPERTY_ATTRIBUTE:
calc = CALCULATION_PROPERTY_TYPES[typ](**calculation_object)
else:
calc = CALCULATION_TYPES[typ](**calculation_object)
if index is None:
obj[attribute] = calc
@ -1025,7 +1141,7 @@ class RougailConvert(ParserVariable):
path_prefix: Optional[str] = None,
) -> None:
"""Parse directories content"""
self.init()
self._init()
if path_prefix:
if path_prefix in self.parents:
raise Exception("pfffff")
@ -1039,28 +1155,54 @@ class RougailConvert(ParserVariable):
"",
False,
None,
None,
'',
)
else:
root_parent = "."
if self.main_namespace:
directory_dict = chain(
(
(
self.rougailconfig["variable_namespace"],
self.rougailconfig["dictionaries_dir"],
self.main_namespace,
self.main_dictionaries,
),
),
self.rougailconfig["extra_dictionaries"].items(),
self.extra_dictionaries.items(),
)
for namespace, extra_dirs in directory_dict:
if namespace is None:
self.namespace = namespace
else:
self.namespace = normalize_family(namespace)
if root_parent == ".":
namespace_path = self.namespace
else:
namespace_path = f"{root_parent}.{self.namespace}"
if namespace_path in self.parents:
raise Exception("pfff")
for filename in self.get_sorted_filename(extra_dirs):
for idx, filename in enumerate(self.get_sorted_filename(extra_dirs)):
if not idx:
self.parse_family(
'',
self.namespace,
namespace_path,
{'description': namespace,
},
'',
)
self.parse_variable_file(
filename,
namespace_path,
)
else:
self.namespace = None
if root_parent == ".":
namespace_path = ''
else:
namespace_path = f"{root_parent}"
if namespace_path in self.parents:
raise Exception("pfff")
for filename in self.get_sorted_filename(self.main_dictionaries):
self.parse_variable_file(
filename,
namespace_path,
@ -1093,13 +1235,20 @@ class RougailConvert(ParserVariable):
objects,
filename,
)
self.parse_family(
filename,
self.namespace,
if objects is None:
return
self.parse_root_file(filename,
path,
{},
version,
objects,
)
def parse_root_file(self,
filename: str,
path: str,
version: str,
objects: dict,
) -> None:
for name, obj in objects.items():
comment = self.get_comment(name, objects)
self.family_or_variable(
@ -1124,7 +1273,7 @@ class RougailConvert(ParserVariable):
continue
filenames = {}
for file_path in directory.iterdir():
if not file_path.suffix == ".yml":
if file_path.suffix not in [".yml", ".yaml"]:
continue
if file_path.name in filenames:
raise DictConsistencyError(
@ -1142,6 +1291,8 @@ class RougailConvert(ParserVariable):
filename: str,
) -> None:
"""version is mandatory in YAML file"""
if obj is None:
obj = {}
for name in ["_version", "version"]:
if name not in obj:
continue
@ -1149,7 +1300,7 @@ class RougailConvert(ParserVariable):
break
else:
# the `version` attribute is not mandatory
default_version = self.rougailconfig["default_dictionary_format_version"]
default_version = self.default_dictionary_format_version
if default_version is not None:
version = default_version
else:
@ -1176,15 +1327,12 @@ class RougailConvert(ParserVariable):
def reflect(self) -> None:
"""Apply TiramisuReflector"""
functions_file = self.rougailconfig["functions_file"]
if not isinstance(functions_file, list):
functions_file = [functions_file]
functions_file = [
func for func in functions_file if func not in self.exclude_imports
functions_files = [
func for func in self.functions_files if func not in self.exclude_imports
]
self.reflector = TiramisuReflector(
self,
functions_file,
functions_files,
)
def save(
@ -1192,11 +1340,12 @@ class RougailConvert(ParserVariable):
filename: str,
):
"""Return tiramisu object declaration as a string"""
self._init()
self.annotate()
self.reflect()
output = self.reflector.get_text() + "\n"
if filename:
with open(filename, "w", encoding="utf-8") as tiramisu:
tiramisu.write(output)
# print(output)
#print(output)
return output

View file

@ -79,3 +79,8 @@ class UpgradeError(Exception):
class NotFoundError(Exception):
"not found error"
pass
## ---- specific exceptions ----
class VariableCalculationDependencyError(Exception):
pass

View file

@ -29,8 +29,9 @@ from pydantic import (
StrictStr,
ConfigDict,
)
from tiramisu import undefined
from .utils import get_jinja_variable_to_param, get_realpath
from .error import DictConsistencyError
from .error import DictConsistencyError, VariableCalculationDependencyError
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
PROPERTY_ATTRIBUTE = ["frozen", "hidden", "disabled", "mandatory"]
@ -45,46 +46,63 @@ def convert_boolean(value: str) -> bool:
return True
elif value == "false":
return False
elif value in ['', None]:
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),
"string": dict(opttype="StrOption", example="example"),
"number": dict(opttype="IntOption", func=int, example=42),
"float": dict(opttype="FloatOption", func=float, example=1.42),
"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"),
"secret": dict(opttype="PasswordOption", example="secrets"),
"mail": dict(opttype="EmailOption", example="user@example.net"),
"unix_filename": dict(opttype="FilenameOption", example="/tmp/myfile.txt"),
"date": dict(opttype="DateOption", example="2000-01-01"),
"unix_user": dict(opttype="UsernameOption", example="username"),
"ip": dict(
opttype="IPOption", initkwargs={"allow_reserved": True}, example="1.1.1.1"
),
"cidr": dict(opttype="IPOption", initkwargs={"cidr": True}, example="1.1.1.0/24"),
"netmask": dict(opttype="NetmaskOption", example="255.255.255.0"),
"network": dict(opttype="NetworkOption", example="1.1.1.0"),
"network_cidr": dict(
opttype="NetworkOption", initkwargs={"cidr": True}, example="1.1.1.0/24"
),
"broadcast": dict(opttype="BroadcastOption", example="1.1.1.255"),
"netbios": dict(
opttype="DomainnameOption",
initkwargs={"type": "netbios", "warnings_only": True},
example="example",
),
"domainname": dict(
opttype="DomainnameOption", initkwargs={"type": "domainname", "allow_ip": False}
opttype="DomainnameOption",
initkwargs={"type": "domainname", "allow_ip": False},
example="example.net",
),
"hostname": dict(
opttype="DomainnameOption", initkwargs={"type": "hostname", "allow_ip": False}
opttype="DomainnameOption",
initkwargs={"type": "hostname", "allow_ip": False},
example="example",
),
"web_address": dict(
opttype="URLOption", initkwargs={"allow_ip": False, "allow_without_dot": True}
opttype="URLOption",
initkwargs={"allow_ip": False, "allow_without_dot": True},
example="https://example.net",
),
"port": dict(opttype="PortOption", initkwargs={"allow_private": True}),
"mac": dict(opttype="MACOption"),
"port": dict(
opttype="PortOption", initkwargs={"allow_private": True}, example="111"
),
"mac": dict(opttype="MACOption", example="00:00:00:00:00"),
"unix_permissions": dict(
opttype="PermissionsOption", initkwargs={"warnings_only": True}, func=int
opttype="PermissionsOption",
initkwargs={"warnings_only": True},
func=int,
example="644",
),
"choice": dict(opttype="ChoiceOption"),
"choice": dict(opttype="ChoiceOption", example="a_choice"),
"regexp": dict(opttype="RegexpOption"),
#
"symlink": dict(opttype="SymLinkOption"),
}
@ -94,7 +112,8 @@ class Param(BaseModel):
key: str
model_config = ConfigDict(extra="forbid")
def __init__(self,
def __init__(
self,
path,
attribute,
family_is_dynamic,
@ -107,13 +126,14 @@ class Param(BaseModel):
class AnyParam(Param):
type: str
value: BASETYPE
value: Union[BASETYPE, List[BASETYPE]]
class VariableParam(Param):
type: str
variable: str
propertyerror: bool = True
whole: bool = False
optional: bool = False
@ -121,12 +141,13 @@ class SuffixParam(Param):
type: str
suffix: Optional[int] = None
def __init__(self,
def __init__(
self,
**kwargs,
) -> None:
if not kwargs['family_is_dynamic']:
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'])
raise DictConsistencyError(msg, 10, kwargs["xmlfiles"])
super().__init__(**kwargs)
@ -139,17 +160,17 @@ class InformationParam(Param):
class IndexParam(Param):
type: str
def __init__(self,
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'])
raise DictConsistencyError(msg, 25, kwargs["xmlfiles"])
super().__init__(**kwargs)
PARAM_TYPES = {
"any": AnyParam,
"variable": VariableParam,
@ -164,7 +185,9 @@ class Calculation(BaseModel):
path: str
inside_list: bool
version: str
namespace: str
ori_path: Optional[str] = None
default_values: Any = None
namespace: Optional[str]
xmlfiles: List[str]
model_config = ConfigDict(extra="forbid")
@ -182,12 +205,22 @@ class Calculation(BaseModel):
for param_obj in self.params:
param = param_obj.model_dump()
if param.get("type") == "variable":
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, suffix = objectspace.paths.get_with_dynamic(
param["variable"], self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
param["variable"],
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if not variable:
if not param.get("optional"):
raise Exception(f"cannot find {param['variable']}")
msg = f'cannot find variable "{param["variable"]}" defined attribute in "{self.attribute_name}" for "{self.path}"'
raise DictConsistencyError(msg, 22, self.xmlfiles)
continue
if not isinstance(variable, objectspace.variable):
raise Exception("pfff it's a family")
@ -196,8 +229,17 @@ class Calculation(BaseModel):
param["suffix"] = suffix
if param.get("type") == "information":
if param["variable"]:
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, suffix = objectspace.paths.get_with_dynamic(
param["variable"], self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
param["variable"],
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if not variable:
msg = f'cannot find variable "{param["variable"]}" defined in "{self.attribute_name}" for "{self.path}"'
@ -214,11 +256,20 @@ class Calculation(BaseModel):
class JinjaCalculation(Calculation):
attribute_name: Literal[
"frozen", "hidden", "mandatory", "disabled", "default", "validators", "choices", "dynamic"
"frozen",
"hidden",
"mandatory",
"empty",
"disabled",
"default",
"validators",
"choices",
"dynamic",
]
jinja: StrictStr
params: Optional[List[Param]] = None
return_type: BASETYPE = None
description: Optional[StrictStr] = None
def _jinja_to_function(
self,
@ -243,16 +294,25 @@ class JinjaCalculation(Calculation):
"__internal_jinja": jinja_path,
"__internal_type": return_type,
"__internal_multi": multi,
"__internal_files": self.xmlfiles,
"__internal_attribute": self.attribute_name,
"__internal_variable": self.path,
},
}
if self.default_values:
default["params"]["__default_value"] = self.default_values
if add_help:
default["help"] = function + "_help"
if self.params:
default["params"] |= self.get_params(objectspace)
if params:
default["params"] |= params
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
for sub_variable, suffix, true_path in get_jinja_variable_to_param(
self.path,
path,
self.jinja,
objectspace,
variable.xmlfiles,
@ -261,7 +321,14 @@ class JinjaCalculation(Calculation):
self.version,
self.namespace,
):
if sub_variable.path in objectspace.variables:
if true_path in default["params"]:
continue
if isinstance(sub_variable, dict):
default["params"][true_path] = {
"type": "value",
"value": sub_variable,
}
else:
default["params"][true_path] = {
"type": "variable",
"variable": sub_variable,
@ -309,7 +376,7 @@ class JinjaCalculation(Calculation):
False,
objectspace,
add_help=True,
params={None: [self.attribute_name]},
params={None: [self.attribute_name], "when": True, "inverse": False},
)
elif self.attribute_name == "choices":
return_type = self.return_type
@ -331,27 +398,42 @@ class JinjaCalculation(Calculation):
raise Exception("hu?")
class VariableCalculation(Calculation):
attribute_name: Literal[
"frozen", "hidden", "mandatory", "disabled", "default", "choices", "dynamic"
]
class _VariableCalculation(Calculation):
variable: StrictStr
propertyerror: bool = True
allow_none: bool = False
def to_function(
def get_variable(self,
objectspace,
) -> "Variable":
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, suffix = objectspace.paths.get_with_dynamic(
self.variable,
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if variable and not isinstance(variable, objectspace.variable):
# FIXME remove the pfff
raise Exception("pfff it's a family")
return variable, suffix
def get_params(
self,
objectspace,
) -> dict:
variable, suffix = objectspace.paths.get_with_dynamic(
self.variable, self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
)
variable: "Variable",
suffix: Optional[str],
*,
needs_multi: Optional[bool] = None,
):
if not variable:
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):
# FIXME remove the pfff
raise Exception("pfff it's a family")
param = {
"type": "variable",
"variable": variable,
@ -360,24 +442,31 @@ class VariableCalculation(Calculation):
if suffix:
param["suffix"] = suffix
params = {None: [param]}
function = "calc_value"
help_function = None
if self.attribute_name in PROPERTY_ATTRIBUTE:
function = "variable_to_property"
help_function = "variable_to_property"
if variable.type != "boolean":
raise Exception("only boolean!")
params[None].insert(0, self.attribute_name)
if self.default_values:
params["__default_value"] = self.default_values
if self.allow_none:
params["allow_none"] = True
# current variable is a multi
if self.attribute_name in PROPERTY_ATTRIBUTE:
needs_multi = False
elif self.attribute_name != "default":
if needs_multi is None:
if 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))
calc_variable_is_multi = variable.path in objectspace.multis
if not calc_variable_is_multi:
if variable.path in objectspace.paths._dynamics and (
suffix is None or suffix[-1] is None
):
self_dyn_path = objectspace.paths._dynamics.get(self.path)
if self_dyn_path is not None:
var_dyn_path = objectspace.paths._dynamics[variable.path]
if self_dyn_path != var_dyn_path and not self_dyn_path.startswith(
f"{var_dyn_path}."
):
calc_variable_is_multi = True
else:
calc_variable_is_multi = True
elif suffix and '{{ suffix }}' in suffix:
calc_variable_is_multi = True
if needs_multi:
if calc_variable_is_multi:
if self.inside_list:
@ -390,15 +479,82 @@ class VariableCalculation(Calculation):
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:
if variable.multi or variable.path.rsplit('.', 1)[0] != self.path.rsplit('.', 1)[0]:
# it's not a follower or not in same leadership
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 = {
"function": function,
else:
params[None][0]['index'] = {'index': {'type': 'index'}}
return params
class VariableCalculation(_VariableCalculation):
attribute_name: Literal["default", "choices", "dynamic"]
optional: bool = False
def to_function(
self,
objectspace,
) -> dict:
if self.attribute_name != "default" and self.optional:
msg = f'"{self.attribute_name}" variable shall not have an "optional" attribute for variable "{self.variable}"'
raise DictConsistencyError(msg, 33, self.xmlfiles)
variable, suffix = self.get_variable(objectspace)
if not variable and self.optional:
raise VariableCalculationDependencyError()
params = self.get_params(objectspace,
variable,
suffix,
)
return {
"function": "calc_value",
"params": params,
}
if help_function:
ret["help"] = help_function
return ret
class VariablePropertyCalculation(_VariableCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
when: Any = undefined
when_not: Any = undefined
def to_function(
self,
objectspace,
) -> dict:
variable, suffix = self.get_variable(objectspace)
params = self.get_params(objectspace,
variable,
suffix,
needs_multi=False,)
variable = params[None][0]["variable"]
if self.when is not undefined:
if self.version == "1.0":
msg = f'when is not allowed in format version 1.0 for attribute "{self.attribute_name}" for variable "{self.path}"'
raise DictConsistencyError(msg, 103, variable.xmlfiles)
if self.when_not is not undefined:
msg = f'the variable "{self.path}" has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
raise DictConsistencyError(msg, 31, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
if self.version == "1.0":
msg = f'when_not is not allowed in format version 1.0 for attribute "{self.attribute_name}" for variable "{self.path}"'
raise DictConsistencyError(msg, 104, variable.xmlfiles)
when = self.when_not
inverse = True
else:
if variable.type != "boolean":
raise Exception("only boolean!")
when = True
inverse = False
params[None].insert(0, self.attribute_name)
params["when"] = when
params["inverse"] = inverse
return {
"function": "variable_to_property",
"params": params,
"help": "variable_to_property",
}
class InformationCalculation(Calculation):
@ -410,37 +566,96 @@ class InformationCalculation(Calculation):
self,
objectspace,
) -> dict:
param = {
params = {
None: [
{
"type": "information",
"information": self.information,
}
]
}
if self.variable:
if self.ori_path is None:
path = self.path
else:
path = self.ori_path
variable, suffix = objectspace.paths.get_with_dynamic(
self.variable, self.path_prefix, self.path, self.version, self.namespace, self.xmlfiles
self.variable,
self.path_prefix,
path,
self.version,
self.namespace,
self.xmlfiles,
)
if variable is None or suffix is not None:
raise Exception("pfff")
param["variable"] = variable
params[None][0]["variable"] = variable
if self.default_values:
params["__default_value"] = self.default_values
return {
"function": "calc_value",
"params": {None: [param]},
"params": params,
}
class SuffixCalculation(Calculation):
attribute_name: Literal["default", "choice", "dynamic"]
class _SuffixCalculation(Calculation):
suffix: Optional[int] = None
def get_suffix(self) -> dict:
suffix = {"type": "suffix"}
if self.suffix is not None:
suffix["suffix"] = self.suffix
return suffix
class SuffixCalculation(_SuffixCalculation):
attribute_name: Literal["default", "choice", "dynamic"]
def to_function(
self,
objectspace,
) -> dict:
suffix = {"type": "suffix"}
if self.suffix is not None:
suffix['suffix'] = self.suffix
suffix["suffix"] = self.suffix
return {
"function": "calc_value",
"params": {None: [suffix]},
"params": {None: [self.get_suffix()]},
}
class SuffixPropertyCalculation(_SuffixCalculation):
attribute_name: Literal[*PROPERTY_ATTRIBUTE]
when: Any = undefined
when_not: Any = undefined
def to_function(
self,
objectspace,
) -> dict:
if self.version == "1.0":
msg = f'when is not allowed in format version 1.0 for attribute "{self.attribute_name}"'
raise DictConsistencyError(msg, 105, variable.xmlfiles)
if self.when is not undefined:
if self.when_not is not undefined:
msg = f'the suffix has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
raise DictConsistencyError(msg, 35, variable.xmlfiles)
when = self.when
inverse = False
elif self.when_not is not undefined:
when = self.when_not
inverse = True
else:
msg = f'the suffix has an invalid attribute "{self.attribute_name}", when and when_not cannot set together'
raise DictConsistencyError
params = {None: [self.attribute_name, self.get_suffix()],
"when": when,
"inverse": inverse,
}
return {
"function": "variable_to_property",
"params": params,
"help": "variable_to_property",
}
@ -467,6 +682,13 @@ CALCULATION_TYPES = {
"suffix": SuffixCalculation,
"index": IndexCalculation,
}
CALCULATION_PROPERTY_TYPES = {
"jinja": JinjaCalculation,
"variable": VariablePropertyCalculation,
"information": InformationCalculation,
"suffix": SuffixPropertyCalculation,
"index": IndexCalculation,
}
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, Calculation, None]
@ -480,14 +702,15 @@ class Family(BaseModel):
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
namespace: Optional[str]
version: str
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class Dynamic(Family):
variable: str=None
# None only for format 1.0
variable: str = None
dynamic: Union[List[Union[StrictStr, Calculation]], Calculation]
@ -498,6 +721,7 @@ class Variable(BaseModel):
description: Optional[str] = None
default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None
choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None
regexp: Optional[str] = None
params: Optional[List[Param]] = None
validators: Optional[List[Calculation]] = None
multi: Optional[bool] = None
@ -506,27 +730,28 @@ class Variable(BaseModel):
hidden: Union[bool, Calculation] = False
disabled: Union[bool, Calculation] = False
mandatory: Union[None, bool, Calculation] = True
empty: Union[None, bool, Calculation] = True
auto_save: bool = False
mode: Optional[str] = None
test: Optional[list] = None
examples: Optional[list] = None
path: str
namespace: str
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
#class Choice(Variable):
# type: Literal["choice"] = "choice"
# choices: Union[List[BASETYPE_CALC], Calculation]
class SymLink(BaseModel):
name: str
type: Literal["symlink"] = "symlink"
opt: Variable
xmlfiles: List[str] = []
name: str
path: str
opt: Variable
namespace: Optional[str]
version: str
path_prefix: Optional[str]
xmlfiles: List[str] = []
model_config = ConfigDict(extra="forbid")

View file

@ -0,0 +1,89 @@
"""Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr)
Copyright (C) 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 rougail.annotator.variable import Walk
from rougail.utils import _
from rougail.error import DictConsistencyError
class Annotator(Walk):
"""Annotate value"""
level = 80
def __init__(self, objectspace, *args) -> None:
if not objectspace.paths:
return
self.alternative_names = {}
self.objectspace = objectspace
not_for_commandlines = []
for family in self.get_families():
if family.commandline:
continue
self.not_for_commandline(family)
not_for_commandlines.append(family.path + '.')
for variable in self.get_variables():
if variable.type == 'symlink':
continue
variable_path = variable.path
for family_path in not_for_commandlines:
if variable_path.startswith(family_path):
break
else:
if not variable.commandline:
self.not_for_commandline(variable)
else:
self.manage_alternative_name(variable)
self.manage_negative_description(variable)
def not_for_commandline(self, variable) -> None:
self.objectspace.properties.add(variable.path, 'not_for_commandline', True)
def manage_alternative_name(self, variable) -> None:
if not variable.alternative_name:
return
alternative_name = variable.alternative_name
variable_path = variable.path
all_letters = ''
for letter in alternative_name:
all_letters += letter
if all_letters == 'h':
msg = _(f'alternative_name "{alternative_name}" conflict with "--help"')
raise DictConsistencyError(msg, 202, variable.xmlfiles)
if all_letters in self.alternative_names:
msg = _(f'conflict alternative_name "{alternative_name}": "{variable_path}" and "{self.alternative_names[all_letters]}"')
raise DictConsistencyError(msg, 202, variable.xmlfiles)
self.alternative_names[alternative_name] = variable_path
if '.' not in variable_path:
path = alternative_name
else:
path = variable_path.rsplit('.', 1)[0] + '.' + alternative_name
self.objectspace.add_variable(alternative_name, {'type': 'symlink', 'path': path, 'opt': variable}, variable.xmlfiles, False, False, variable.version)
def manage_negative_description(self, variable) -> None:
if not variable.negative_description:
if variable.type == 'boolean' and not self.objectspace.add_extra_options:
raise DictConsistencyError(_(f'negative_description is mandatory for boolean variable, but "{variable.path}" hasn\'t'), 200, variable.xmlfiles)
return
if variable.type != 'boolean':
raise DictConsistencyError(_(f'negative_description is only available for boolean variable, but "{variable.path}" is "{variable.type}"'), 201, variable.xmlfiles)
self.objectspace.informations.add(
variable.path, "negative_description", variable.negative_description
)

View file

@ -0,0 +1,42 @@
"""
Config file for Rougail-structural_commandline
Silique (https://www.silique.fr)
Copyright (C) 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
"""
def get_rougail_config(*,
backward_compatibility=True,
) -> dict:
options = """
structural_commandline:
description: Configuration rougail-structural_commandline
commandline: false
add_extra_options:
description: Add extra options to tiramisu-cmdline-parser
default: true
"""
return {'name': 'exporter',
'process': 'structural',
'options': options,
'level': 20,
}
__all__ = ('get_rougail_config')

View file

@ -0,0 +1,36 @@
"""Annotate to add specify attribute for tiramisu-cmdline
Silique (https://www.silique.fr)
Copyright (C) 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 Optional
from pydantic import BaseModel
class Variable(BaseModel):
alternative_name: Optional[str]=None
commandline: bool=True
negative_description: Optional[str]=None
class Family(BaseModel):
commandline: bool=True
__all__ = ('Variable', 'Family')

View file

@ -35,16 +35,16 @@ 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 rougail.object_model import CONVERT_OPTION
from rougail.error import display_xmlfiles
from tiramisu.error import ValueWarning, ConfigError
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
func = ENV.filters
ENV.compile_templates('jinja_caches', zip=None)
@ -60,7 +60,14 @@ def load_functions(path):
func[function] = getattr(func_, function)
def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):
def rougail_calc_value(*args, __default_value=None, **kwargs):
values = calc_value(*args, **kwargs)
if __default_value is not None and values in [None, []]:
return __default_value
return values
def jinja_to_function(__internal_variable, __internal_attribute, __internal_jinja, __internal_type, __internal_multi, __internal_files, __default_value=None, **kwargs):
global ENV, CONVERT_OPTION
kw = {}
for key, value in kwargs.items():
@ -71,22 +78,40 @@ def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwa
c_kw = c_kw.setdefault(subkey, {})
c_kw[var] = value
else:
if key in kw:
raise ConfigError(f'internal error, multi key for "{key}" in jinja_to_function')
kw[key] = value
try:
values = ENV.get_template(__internal_jinja).render(kw, **func).strip()
except Exception as err:
raise ConfigError(f'cannot calculating "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}') from err
convert = CONVERT_OPTION[__internal_type].get('func', str)
if __internal_multi:
return [convert(val) for val in values.split()]
values = [convert(val) for val in values.split('\n') if val != ""]
if not values and __default_value is not None:
return __default_value
return values
try:
values = convert(values)
return values if values != '' and values != 'None' else None
except Exception as err:
raise ConfigError(f'cannot converting "{__internal_attribute}" attribute for variable "{__internal_variable}" in {display_xmlfiles(__internal_files)}: {err}') from err
values = values if values != '' and values != 'None' else None
if values is None and __default_value is not None:
return __default_value
return values
def variable_to_property(prop, value):
return prop if value else None
def variable_to_property(prop, value, when, inverse):
if inverse:
is_match = value != when
else:
is_match = value == when
return prop if is_match else None
def jinja_to_property(prop, **kwargs):
def jinja_to_property(prop, when, inverse, **kwargs):
value = func['jinja_to_function'](**kwargs)
return func['variable_to_property'](prop, value is not None)
return func['variable_to_property'](prop, value is not None, when, inverse)
def jinja_to_property_help(prop, **kwargs):
@ -104,6 +129,7 @@ def valid_with_jinja(warnings_only=False, **kwargs):
raise ValueError(value)
func['calc_value'] = rougail_calc_value
func['jinja_to_function'] = jinja_to_function
func['jinja_to_property'] = jinja_to_property
func['jinja_to_property_help'] = jinja_to_property_help
@ -135,3 +161,12 @@ class ConvertDynOptionDescription(DynOptionDescription):
if "{{ suffix }}" in name:
return name.replace("{{ suffix }}", path_suffix)
return name + path_suffix
def impl_get_display_name(
self,
subconfig,
) -> str:
display = super().impl_get_display_name(subconfig)
if "{{ suffix }}" in display:
return display.replace("{{ suffix }}", self.convert_suffix_to_path(self.get_suffixes(subconfig)[-1]))
return display

View file

@ -33,7 +33,7 @@ from json import dumps
from os.path import isfile, basename
from .i18n import _
from .error import DictConsistencyError
from .error import DictConsistencyError, VariableCalculationDependencyError
from .utils import normalize_family
from .object_model import Calculation, CONVERT_OPTION
@ -60,35 +60,37 @@ class TiramisuReflector:
funcs_paths,
):
self.informations_idx = -1
self.rougailconfig = objectspace.rougailconfig
self.reflector_objects = {}
self.text = {
"header": [],
"option": [],
}
if self.rougailconfig["export_with_import"]:
if self.rougailconfig["internal_functions"]:
for func in self.rougailconfig["internal_functions"]:
self.objectspace = objectspace
if self.objectspace.export_with_import:
if self.objectspace.internal_functions:
for func in self.objectspace.internal_functions:
self.text["header"].append(f"func[func] = func")
self.text["header"].extend(
[
"from tiramisu import *",
"from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
"from re import compile as re_compile",
]
)
if self.objectspace.export_with_import:
self.text["header"].extend(
[
"from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription"
]
)
if funcs_paths:
if self.rougailconfig["export_with_import"]:
self.text["header"].extend(
["from rougail.tiramisu import func, dict_env, load_functions, ConvertDynOptionDescription"]
)
for funcs_path in sorted(funcs_paths, key=sorted_func_name):
if not isfile(funcs_path):
continue
self.text["header"].append(f"load_functions('{funcs_path}')")
if self.rougailconfig["export_with_import"]:
for mode in self.rougailconfig["modes_level"]:
if self.objectspace.export_with_import:
for mode in self.objectspace.modes_level:
self.text["header"].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
self.objectspace = objectspace
self.make_tiramisu_objects()
for key, value in self.objectspace.jinja.items():
self.add_jinja_to_function(key, value)
@ -106,14 +108,11 @@ class TiramisuReflector:
baseelt = BaseElt()
self.objectspace.reflector_names[
baseelt.path
] = f'option_0{self.rougailconfig["suffix"]}'
] = f"option_0{self.objectspace.suffix}"
basefamily = Family(
baseelt,
self,
)
# FIXMEif not self.objectspace.paths.has_path_prefix():
if 1:
# for elt in self.reorder_family(self.objectspace.space):
for elt in self.objectspace.paths.get():
if elt.path in self.objectspace.families:
Family(
@ -125,35 +124,35 @@ class TiramisuReflector:
elt,
self,
)
else:
path_prefixes = self.objectspace.paths.get_path_prefixes()
for path_prefix in path_prefixes:
space = self.objectspace.space.variables[path_prefix]
self.set_name(space)
baseprefix = Family(
space,
self,
)
basefamily.add(baseprefix)
for elt in self.reorder_family(space):
self.populate_family(
baseprefix,
elt,
)
if not hasattr(baseprefix.elt, "information"):
baseprefix.elt.information = self.objectspace.information(
baseprefix.elt.xmlfiles
)
for key, value in self.objectspace.paths.get_providers_path(
path_prefix
).items():
setattr(baseprefix.elt.information, key, value)
for key, value in self.objectspace.paths.get_suppliers_path(
path_prefix
).items():
setattr(baseprefix.elt.information, key, value)
baseelt.name = normalize_family(self.rougailconfig["base_option_name"])
baseelt.description = self.rougailconfig["base_option_name"]
# else:
# path_prefixes = self.objectspace.paths.get_path_prefixes()
# for path_prefix in path_prefixes:
# space = self.objectspace.space.variables[path_prefix]
# self.set_name(space)
# baseprefix = Family(
# space,
# self,
# )
# basefamily.add(baseprefix)
# for elt in self.reorder_family(space):
# self.populate_family(
# baseprefix,
# elt,
# )
# if not hasattr(baseprefix.elt, "information"):
# baseprefix.elt.information = self.objectspace.information(
# baseprefix.elt.xmlfiles
# )
# for key, value in self.objectspace.paths.get_providers_path(
# path_prefix
# ).items():
# setattr(baseprefix.elt.information, key, value)
# for key, value in self.objectspace.paths.get_suppliers_path(
# path_prefix
# ).items():
# setattr(baseprefix.elt.information, key, value)
baseelt.name = normalize_family(self.objectspace.base_option_name)
baseelt.description = self.objectspace.base_option_name
self.reflector_objects[baseelt.path].get(
[], baseelt.description
) # pylint: disable=E1101
@ -205,7 +204,9 @@ class Common:
self.populate_attrib()
if self.informations:
for information in self.informations:
self.tiramisu.text['option'].append(f'{information}.set_option({self.option_name})')
self.tiramisu.text["option"].append(
f"{information}.set_option({self.option_name})"
)
return self.option_name
def populate_attrib(self):
@ -279,7 +280,7 @@ class Common:
informations = self.objectspace.informations.get(self.elt.path)
if not informations:
return
keys['informations'] = informations
keys["informations"] = informations
def populate_param(
self,
@ -292,7 +293,10 @@ class Common:
else:
value = param
return f"ParamValue({value})"
if param["type"] == "value":
return f"ParamValue({param['value']})"
if param["type"] == "information":
# default? really?
if self.elt.multi:
default = []
else:
@ -301,14 +305,20 @@ class Common:
if param["variable"].path == self.elt.path:
return f'ParamSelfInformation("{param["information"]}", {default})'
information_variable_path = param["variable"].path
information_variable = self.tiramisu.reflector_objects[information_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 = (
f'ParamInformation("{param["information"]}", {default})'
)
information_name = self.tiramisu.get_information_name()
self.tiramisu.text["option"].append(f'{information_name} = {information}')
self.tiramisu.text["option"].append(
f"{information_name} = {information}"
)
information_variable.informations.append(information_name)
return information_name
return f'ParamInformation("{param["information"]}", {default})'
@ -324,6 +334,7 @@ class Common:
param.get("propertyerror", True),
param.get("suffix"),
param.get("dynamic"),
param.get('whole', False),
)
if param["type"] == "any":
if isinstance(param["value"], str):
@ -335,21 +346,25 @@ class Common:
def build_option_param(
self,
param,
variable,
propertyerror,
suffix: Optional[str],
dynamic,
whole: bool,
) -> str:
"""build variable parameters"""
if param.path == self.elt.path:
return "ParamSelfOption(whole=False)"
option_name = self.tiramisu.reflector_objects[param.path].get(
if variable.path == self.elt.path:
return f"ParamSelfOption(whole={whole})"
if whole:
msg = f'variable param "{variable.path}" has whole attribute but it\'s not allowed for external variable'
raise DictConsistencyError(msg, 34, self.elt.xmlfiles)
option_name = self.tiramisu.reflector_objects[variable.path].get(
self.calls, self.elt.path
)
params = [f"{option_name}"]
if suffix is not None:
param_type = "ParamDynOption"
params.append(str(suffix))
params.append(self.convert_str(suffix))
else:
param_type = "ParamOption"
if not propertyerror:
@ -387,9 +402,10 @@ class Common:
ret = ret + ")"
return ret
def populate_calculation(self,
def populate_calculation(
self,
datas: Union[Calculation, str, list],
return_a_tuple: bool=False,
return_a_tuple: bool = False,
) -> str:
if isinstance(datas, str):
return self.convert_str(datas)
@ -400,15 +416,18 @@ class Common:
params = []
for idx, data in enumerate(datas):
if isinstance(data, Calculation):
try:
params.append(self.calculation_value(data))
except VariableCalculationDependencyError:
pass
elif isinstance(data, str):
params.append(self.convert_str(data))
else:
params.append(str(data))
if return_a_tuple:
ret = '('
ret = "("
else:
ret = '['
ret = "["
ret += ", ".join(params)
if return_a_tuple:
if len(params) <= 1:
@ -428,8 +447,8 @@ class Variable(Common):
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__
if elt.type in self.tiramisu.objectspace.custom_types:
self.object_type = self.tiramisu.objectspace.custom_types[elt.type].__name__
else:
self.object_type = CONVERT_OPTION[elt.type]["opttype"]
@ -441,19 +460,37 @@ class Variable(Common):
keys["opt"] = self.tiramisu.reflector_objects[self.elt.opt.path].get(
self.calls, self.elt.path
)
return
if self.elt.type == "choice":
keys["values"] = self.populate_calculation(self.elt.choices, return_a_tuple=True)
keys["values"] = self.populate_calculation(
self.elt.choices, return_a_tuple=True
)
if self.elt.type == 'regexp':
self.object_type = 'Regexp_' + self.option_name
self.tiramisu.text['header'].append(f'''class {self.object_type}(RegexpOption):
__slots__ = tuple()
_type = 'value'
{self.object_type}._regexp = re_compile(r"{self.elt.regexp}")
''')
if self.elt.path in self.objectspace.multis:
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:
try:
keys["default"] = self.populate_calculation(self.elt.default)
except VariableCalculationDependencyError:
pass
if self.elt.path in self.objectspace.default_multi:
keys["default_multi"] = self.populate_calculation(self.objectspace.default_multi[self.elt.path])
try:
keys["default_multi"] = self.populate_calculation(
self.objectspace.default_multi[self.elt.path]
)
except VariableCalculationDependencyError:
pass
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():
for key, value in (
CONVERT_OPTION.get(self.elt.type, {}).get("initkwargs", {}).items()
):
if isinstance(value, str):
value = self.convert_str(value)
keys[key] = value

View file

@ -0,0 +1,21 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 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 .update import RougailUpgrade

View file

@ -1,4 +1,4 @@
"""Update Rougail XML file to new version
"""Update Rougail structure file to new version
Cadoles (http://www.cadoles.com)
Copyright (C) 2021
@ -23,7 +23,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from os import listdir, makedirs
from os import listdir
from os.path import basename, isdir, isfile, join
from typing import Any, List, Optional, Tuple
@ -35,15 +35,14 @@ except ModuleNotFoundError as err:
# from ast import parse as ast_parse
from json import dumps
from ruamel.yaml import YAML
from pathlib import Path
from ruamel.yaml import YAML
from .config import RougailConfig
from .error import UpgradeError
from .i18n import _
from .object_model import CONVERT_OPTION
from .utils import normalize_family
from ..config import RougailConfig
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"]
@ -64,7 +63,7 @@ class upgrade_010_to_10:
xmlsrc: str,
) -> None:
self.xmlsrc = xmlsrc
self.paths = {"family": {}, "variable": {}}
self.paths = {"family": {}, "variable": {}, 'dynamic': {}}
self.lists = {
"service": {},
"ip": {},
@ -72,7 +71,7 @@ class upgrade_010_to_10:
"file": {},
}
self.flatten_paths = {"family": {}, "variable": {}}
self.variables = self.parse_variables(dico, namespace)
self.variables = self.parse_variables(dico, namespace, namespace, root=True)
self.parse_variables_with_path()
self.parse_services(dico)
self.parse_constraints(dico)
@ -81,13 +80,20 @@ class upgrade_010_to_10:
self,
family: dict,
sub_path: str,
true_sub_path: str,
*,
root: bool=False,
is_dynamic: bool=False,
) -> dict:
new_families = {}
if root:
new_families['version'] = None
if "variables" in family:
for subelt in family["variables"]:
for typ, obj in subelt.items():
for subobj in obj:
getattr(self, f"convert_{typ}")(subobj, new_families, sub_path)
local_is_dynamic = is_dynamic or subobj.get('dynamic') is not None
getattr(self, f"convert_{typ}")(subobj, new_families, sub_path, true_sub_path, local_is_dynamic)
family.pop("variables")
return new_families
@ -96,9 +102,17 @@ class upgrade_010_to_10:
family: dict,
new_families: dict,
sub_path: str,
true_sub_path: str,
is_dynamic: bool,
) -> None:
# name is the key, do not let it in values
name = family.pop("name")
if true_sub_path:
true_sub_path = true_sub_path + "." + name
else:
true_sub_path = name
if is_dynamic:
name += '{{ suffix }}'
if sub_path:
sub_path = sub_path + "." + name
else:
@ -112,7 +126,7 @@ class upgrade_010_to_10:
if typ == "dynamic":
family["variable"] = self.get_variable_path(value)
# add sub families and sub variables
sub_families = self.parse_variables(family, sub_path)
sub_families = self.parse_variables(family, sub_path, true_sub_path, is_dynamic=is_dynamic)
for sub_name, sub_family in sub_families.copy().items():
if sub_name not in family:
continue
@ -128,12 +142,23 @@ class upgrade_010_to_10:
variable: dict,
new_families: dict,
sub_path: str,
true_sub_path: str,
is_dynamic: bool,
) -> dict:
name = variable.pop("name")
if is_dynamic:
if true_sub_path:
true_sub_path = true_sub_path + "." + name
else:
true_sub_path = name
self.flatten_paths["variable"][name] = true_sub_path
name += '{{ suffix }}'
if sub_path:
sub_path = sub_path + "." + name
else:
sub_path = name
if is_dynamic:
self.paths['dynamic'][true_sub_path] = sub_path
new_families[name] = variable
self.flatten_paths["variable"][name] = sub_path
self.paths["variable"][sub_path] = variable
@ -173,24 +198,32 @@ class upgrade_010_to_10:
)(test)
)
variable["test"] = tests
if variable.get('mandatory', False):
del variable["mandatory"]
CONVERT_TYPE = {'filename': 'unix_filename',
'password': 'secret',
}
if variable.get('type') in CONVERT_TYPE:
variable['type'] = CONVERT_TYPE[variable['type']]
def parse_variables_with_path(self):
for variable in self.paths["variable"].values():
multi = variable.get('multi', False)
if "value" in variable:
default = variable.pop("value")
if default is not None:
if not variable.get("multi", False) and len(default) == 1:
variable["default"] = self.get_value(default[0])
if not multi and len(default) == 1:
variable["default"] = self.get_value(default[0], multi)
else:
variable["default"] = [
self.get_value(value) for value in default
self.get_value(value, multi) for value in default
]
if "choice" in variable:
if not variable["choice"]:
variable["choices"] = variable.pop("choice")
else:
variable["choices"] = [
self.get_value(choice) for choice in variable.pop("choice")
self.get_value(choice, multi) for choice in variable.pop("choice")
]
def parse_services(
@ -306,15 +339,32 @@ class upgrade_010_to_10:
apply_on_fallback = False
source = self.get_variable_path(condition["source"])
if not source:
source = f'__{condition["source"]}'
source = condition["source"]
name = condition.pop("name")
prop = name.split("_", 1)[0]
multi = False
for target in condition["target"]:
typ = target.get("type", "variable")
if typ == "variable":
variable_path = self.get_variable_path(target["text"])
if variable_path is None:
continue
variable = self.paths["variable"][variable_path]
if variable.get('multi', False):
multi = True
if apply_on_fallback:
condition_value = True
else:
if "{{ suffix }}" in source:
force_param = {'__var': source}
source = '__var'
else:
force_param = None
condition_value = self.params_condition_to_jinja(
prop, source, condition["param"], name.endswith("if_in")
prop, source, condition["param"], name.endswith("if_in"), multi
)
if force_param:
condition_value.setdefault('params', {}).update(force_param)
for target in condition["target"]:
typ = target.get("type", "variable")
if typ == "variable":
@ -367,7 +417,7 @@ class upgrade_010_to_10:
check["param"] = [
{"text": variable_path, "type": "variable"}
] + check.get("param", [])
check_value = self.convert_param_function(check)
check_value = self.convert_param_function(check, variable.get('multi', False))
variable.setdefault("validators", []).append(check_value)
def parse_fill(
@ -377,8 +427,9 @@ class upgrade_010_to_10:
for target in fill.pop("target"):
params = []
variable_path = self.get_variable_path(target["text"])
if variable_path is None:
continue
if variable_path in self.paths["dynamic"]:
variable_path = self.paths["dynamic"][variable_path]
if variable_path in self.paths["variable"]:
variable = self.paths["variable"][variable_path]
if fill.get("type") == "jinja":
fill_value = {
@ -386,8 +437,12 @@ class upgrade_010_to_10:
"jinja": fill["name"],
}
else:
fill_value = self.convert_param_function(fill)
fill_value = self.convert_param_function(fill, variable.get('multi', False))
variable["default"] = fill_value
if variable.get('mandatory') is False:
del variable['mandatory']
else:
raise Exception(f'cannot set fill to unknown variable "{variable_path}"')
def params_condition_to_jinja(
self,
@ -395,6 +450,7 @@ class upgrade_010_to_10:
path: str,
params: List[dict],
if_in: bool,
multi: bool,
) -> str:
new_params = {}
jinja = "{% if "
@ -402,7 +458,7 @@ class upgrade_010_to_10:
if idx:
jinja += " or "
new_param, value = self.get_jinja_param_and_value(param)
new_param, value = self.get_jinja_param_and_value(param, multi)
if value:
jinja += path + " == " + value
if new_param:
@ -422,10 +478,11 @@ class upgrade_010_to_10:
def get_value(
self,
param: dict,
multi: bool,
) -> Any:
typ = param.get("type", "string")
if typ == "string":
value = param["text"]
value = param.get("text")
elif typ == "number":
value = int(param["text"])
elif typ == "nil":
@ -447,7 +504,7 @@ class upgrade_010_to_10:
if "propertyerror" in param:
value["propertyerror"] = param["propertyerror"]
elif typ == "function":
value = self.convert_param_function(param)
value = self.convert_param_function(param, multi)
elif typ == "information":
value = {
"type": "information",
@ -465,10 +522,11 @@ class upgrade_010_to_10:
def get_jinja_param_and_value(
self,
param,
multi: bool,
) -> Tuple[list, Any]:
new_param = None
typ = param.get("type", "string")
value = self.get_value(param)
value = self.get_value(param, multi)
if isinstance(value, dict):
if typ == "information":
key = normalize_family(value["information"])
@ -476,17 +534,25 @@ class upgrade_010_to_10:
attr_name = f'{value["variable"]}.{key}'
else:
attr_name = key
attr_name = f"__information.{attr_name}"
attr_name = f"__information_{attr_name}"
new_param = {attr_name: value}
value = attr_name
elif typ in ["index", "suffix"]:
if 'name' in value:
attr_name = value['name']
else:
attr_name = f"__{typ}"
new_param = {attr_name: value}
new_param = {attr_name: {"type": typ}}
value = attr_name
elif "propertyerror" in param or "optional" in param:
attr_name = value["variable"]
new_param = {attr_name: value}
value = value[typ]
elif "{{ suffix }}" in value[typ]:
path = value[typ]
attr_name = path.split('.')[-1][:-12] # remove {{ suffix }}
new_param = {attr_name: value}
value = attr_name
else:
value = value[typ]
if not value:
@ -498,22 +564,35 @@ class upgrade_010_to_10:
def convert_param_function(
self,
param: dict,
multi: bool,
) -> str:
text = param["name"]
params = {}
# multi = False
if "param" in param and param["param"]:
if text == 'calc_value' and len(param["param"]) == 1 and isinstance(param["param"][0], dict) and param["param"][0].get('type') == 'variable' and param["param"][0].get("text"):
value = param["param"][0]["text"]
path = self.get_variable_path(value)
if not path:
path = value
ret = {"type": "variable", "variable": path}
if 'optional' in param["param"][0]:
ret['optional'] = param["param"][0]["optional"]
return ret
first, *others = param["param"]
new_param, first = self.get_jinja_param_and_value(first)
new_param, first = self.get_jinja_param_and_value(first, multi)
text = f"{first} | {text}"
if new_param:
params |= new_param
if others:
values = []
for param in others:
new_param, value = self.get_jinja_param_and_value(param)
new_param, value = self.get_jinja_param_and_value(param, multi)
if new_param:
params |= new_param
if "name" in param:
if param["name"] == "multi" and value == "true":
multi = True
values.append(f'{param["name"]}={value}')
else:
values.append(value)
@ -522,7 +601,12 @@ class upgrade_010_to_10:
text += ")"
else:
text += "()"
if not multi:
text = "{{ " + text + " }}"
else:
text = """{% for __variable in """ + text + """ %}
{{ __variable }}
{% endfor %}"""
ret = {"type": "jinja", "jinja": text}
if params:
ret["params"] = params
@ -537,8 +621,10 @@ class upgrade_010_to_10:
and path in self.flatten_paths["variable"]
):
path = self.flatten_paths["variable"][path]
if path in self.paths["dynamic"]:
path = self.paths["dynamic"][path]
if path not in self.paths["variable"]:
return
return path
return path
def get_family_path(
@ -570,43 +656,38 @@ class RougailUpgrade:
rougailconfig = RougailConfig
self.rougailconfig = rougailconfig
def load_dictionaries(
def run(
self,
dstfolder: str,
services_dstfolder: Optional[str] = None,
extra_dstfolder: Optional[str] = None,
):
if extra_dstfolder is None:
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:
for dict_dir, dest_dir in zip(self.rougailconfig["main_dictionaries"], self.rougailconfig["upgrade_options.main_dictionaries"]):
self._load_dictionaries(
dict_dir,
dstfolder,
services_dstfolder,
self.rougailconfig["variable_namespace"],
dest_dir,
normalize_family(self.rougailconfig["main_namespace"]),
)
if self.rougailconfig['main_namespace']:
if self.rougailconfig["extra_dictionaries"]:
dst_extra_dir = self.rougailconfig["upgrade_options.extra_dictionary"]
for namespace, extra_dirs in self.rougailconfig["extra_dictionaries"].items():
extra_dstsubfolder = join(extra_dstfolder, namespace)
if not isdir(extra_dstsubfolder):
makedirs(extra_dstsubfolder)
extra_dstsubfolder = Path(dst_extra_dir) / namespace
if not extra_dstsubfolder.is_dir():
extra_dstsubfolder.mkdir()
for extra_dir in extra_dirs:
self._load_dictionaries(
extra_dir,
extra_dstsubfolder,
None,
namespace,
str(extra_dir),
str(extra_dstsubfolder),
normalize_family(namespace),
)
def _load_dictionaries(
self,
srcfolder: str,
dstfolder: str,
services_dstfolder: Optional[str],
dstfolder: Optional[str],
namespace: str,
) -> None:
if dstfolder is None:
dstfolder = srcfolder
Path(dstfolder).mkdir(parents=True, exist_ok=True)
filenames = [
filename
for filename in listdir(srcfolder)
@ -615,18 +696,8 @@ class RougailUpgrade:
filenames.sort()
for filename in filenames:
xmlsrc = Path(srcfolder) / Path(filename)
ymlfile = filename[:-3] + "yml"
xmldst = Path(dstfolder) / Path(ymlfile)
if xmldst.is_file():
raise Exception(
f'cannot update "{xmlsrc}" destination file "{xmldst}" already exists'
)
if services_dstfolder:
ymldst_services = Path(services_dstfolder) / ymlfile
if ymldst_services.is_file():
raise Exception(
f'cannot update "{xmlsrc}" destination file "{ymldst_services}" already exists'
)
ymldst = Path(dstfolder) / (Path(filename).stem + '.yml')
if filename.endswith(".xml"):
if parse is None:
raise Exception("XML module is not installed")
@ -641,7 +712,7 @@ class RougailUpgrade:
)
ext = "xml"
else:
with xmlsrc.open() as xml_fh:
with xmlsrc.open() as file_fh:
root = YAML(typ="safe").load(file_fh)
search_function_name = get_function_name(str(root["version"]))
ext = "yml"
@ -661,24 +732,23 @@ class RougailUpgrade:
root_services = root_services_
if function_version == search_function_name:
function_found = True
if root:
root["version"] = version
xmldst.parent.mkdir(parents=True, exist_ok=True)
with xmldst.open("w") as ymlfh:
if root != {'version': None}:
root["version"] = float(version)
with ymldst.open("w") as ymlfh:
yaml = YAML()
yaml.dump(
root,
xmldst,
)
if root_services and services_dstfolder:
root_services["version"] = version
ymldst_services.parent.mkdir(parents=True, exist_ok=True)
with ymldst_services.open("w") as ymlfh:
yaml = YAML()
yaml.dump(
root_services,
ymlfh,
)
# if root_services and services_dstfolder:
# root_services["version"] = version
# ymldst_services.parent.mkdir(parents=True, exist_ok=True)
# with ymldst_services.open("w") as ymlfh:
# yaml = YAML()
# yaml.dump(
# root_services,
# ymlfh,
# )
def _attribut_to_bool(self, variable):
for prop in [
@ -834,23 +904,47 @@ class RougailUpgrade:
return dico
def _update_1_1(self, root):
if not isinstance(root, dict):
return
new_root = {}
update_root = False
for key, value in root.items():
new_root[key] = value
if not isinstance(value, dict):
continue
# migrate dynamic family
if (
("variable" in root and isinstance(root["variable"], str))
or ("_variable" in root and isinstance(root["_variable"], str))
("variable" in value and isinstance(value["variable"], str))
or ("_variable" in value and isinstance(value["_variable"], str))
) and (
("_type" in root and root["_type"] == "dynamic")
or ("type" in root and root["type"] == "dynamic")
("_type" in value and value["_type"] == "dynamic")
or ("type" in value and value["type"] == "dynamic")
):
root["dynamic"] = {
value["dynamic"] = {
"type": "variable",
"variable": root.pop("variable"),
"variable": value.pop("variable"),
"propertyerror": False,
}
for key, value in root.items():
if '{{ suffix }}' not in key:
new_root[key + '{{ suffix }}'] = new_root.pop(key)
update_root = True
self._update_1_1(value)
for typ, obj in {'boolean': bool,
'number': int,
'string': str,
'float': float,
}.items():
if value.get('type') == typ:
default = value.get('default')
if default is None or default == []:
continue
if isinstance(default, obj):
del value['type']
elif isinstance(default, list) and isinstance(default[0], obj):
del value['type']
if value.get('multi') and isinstance(value.get('default'), list):
del value['multi']
if update_root:
root.clear()
root.update(new_root)
def update_1_1(
self,
@ -878,7 +972,7 @@ class RougailUpgrade:
objects = root.find(typ)
if objects is None:
objects = []
new_objects = self._xml_to_yaml(objects, typ, variables, "")
new_objects = self._xml_to_yaml(objects, typ, variables, namespace)
if new_objects[typ]:
new_root.update(new_objects)
else:
@ -928,7 +1022,6 @@ class RougailUpgrade:
if not has_value:
value = SubElement(variable, "value")
value.text = choices[0]
variable.attrib["mandatory"] = "True"
# convert group to leadership
groups = []

View file

@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from typing import List, Union
from unicodedata import normalize, combining
import re
from itertools import chain
from importlib.machinery import SourceFileLoader
from importlib.util import spec_from_loader, module_from_spec
@ -37,7 +38,9 @@ from importlib.util import spec_from_loader, module_from_spec
from jinja2 import DictLoader, TemplateSyntaxError
from jinja2.sandbox import SandboxedEnvironment
from jinja2.parser import Parser
from jinja2.nodes import Getattr
from jinja2.nodes import Name, Getattr
from tiramisu.config import get_common_path
from .i18n import _
from .error import DictConsistencyError
@ -62,15 +65,16 @@ def normalize_family(family_name: str) -> str:
"""replace space, accent, uppercase, ... by valid character"""
if not family_name:
return
family_name = family_name.lower()
family_name = family_name.replace("-", "_").replace(" ", "_").replace(".", "_")
nfkd_form = normalize("NFKD", family_name)
family_name = "".join([c for c in nfkd_form if not combining(c)])
return family_name.lower()
def load_modules(eosfunc_file) -> List[str]:
"""list all functions in eosfunc"""
loader = SourceFileLoader("eosfunc", eosfunc_file)
def load_modules(name, module) -> List[str]:
"""list all functions in a module"""
loader = SourceFileLoader(name, module)
spec = spec_from_loader(loader.name, loader)
eosfunc = module_from_spec(spec)
loader.exec_module(eosfunc)
@ -107,13 +111,18 @@ def get_jinja_variable_to_param(
return g.node.name + "." + g.attr
variables = set()
if objectspace.namespace is None:
for n in parsed_content.find_all(Name):
variables.add(n.name)
for g in parsed_content.find_all(Getattr):
variables.add(recurse_getattr(g))
except TemplateSyntaxError as err:
msg = _(f'error in jinja "{jinja_text}": {err}')
raise Exception(msg) from err
msg = _(f'error in jinja "{jinja_text}" for the variable "{ current_path }": {err}')
raise DictConsistencyError(msg, 39, xmlfiles) from err
variables = list(variables)
variables.sort()
variables.sort(reverse=True)
founded_variables = {}
unknown_variables = []
for variable_path in variables:
variable, suffix = objectspace.paths.get_with_dynamic(
variable_path,
@ -124,4 +133,36 @@ def get_jinja_variable_to_param(
xmlfiles,
)
if variable and variable.path in objectspace.variables:
yield variable, suffix, variable_path
founded_variables[variable_path] = (suffix, variable)
else:
sub_family = variable_path + '.'
for founded_variable in chain(founded_variables, unknown_variables):
if founded_variable.startswith(sub_family):
break
else:
unknown_variables.append(variable_path)
for variable_path in unknown_variables:
for v in founded_variables:
if get_common_path(v, variable_path) == v:
break
else:
root_path = None
vpath = variable_path
while '.' in vpath:
vpath = vpath.rsplit('.', 1)[0]
variable, suffix = objectspace.paths.get_with_dynamic(
vpath,
path_prefix,
current_path,
version,
namespace,
xmlfiles,
)
if variable and variable.path in objectspace.families:
root_path = vpath
break
if root_path:
yield {}, None, root_path
for variable_path, data in founded_variables.items():
yield data[1], data[0], variable_path

View file

@ -0,0 +1,9 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"advanced"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])

View file

@ -0,0 +1,9 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])

View file

@ -0,0 +1,3 @@
---
_version: '1.1'
version: # a variable

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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,15 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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,10 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = StrOption(name="version", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

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

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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,15 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
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,10 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = StrOption(name="empty", doc="empty", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

@ -0,0 +1,10 @@
---
version: 1.1
var1: "no" # a first variable
var2:
description: a second variable
multi: true
default:
jinja: |
{{ _.var1 }}
description: the value of var1

View file

@ -0,0 +1,13 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_rougail.var2'] = "{{ _.var1 }}\n"
option_2 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2, option_3], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,19 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_1.rougail.var2'] = "{{ _.var1 }}\n"
dict_env['default_2.rougail.var2'] = "{{ _.var1 }}\n"
option_3 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_1.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("1.rougail.var2"), '_.var1': ParamOption(option_3)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_7 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_2.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("2.rougail.var2"), '_.var1': ParamOption(option_7)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", children=[option_7, option_8], properties=frozenset({"standard"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -0,0 +1,12 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_var2'] = "{{ _.var1 }}\n"
option_1 = StrOption(name="var1", doc="a first variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -0,0 +1,15 @@
---
version: 1.1
var1: # a first variable
- 'no'
- 'yes'
- maybe
var2:
description: a second variable
multi: true
default:
jinja: |
{% for val in _.var1 %}
{{ val }}
{% endfor %}
description: the value of _.var1

View file

@ -0,0 +1,13 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_2 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("rougail.var2"), '_.var1': ParamOption(option_2)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2, option_3], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,19 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_1.rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
dict_env['default_2.rougail.var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_3 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_1.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("1.rougail.var2"), '_.var1': ParamOption(option_3)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_7 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_2.rougail.var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("2.rougail.var2"), '_.var1': ParamOption(option_7)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", children=[option_7, option_8], properties=frozenset({"standard"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -0,0 +1,12 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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")
dict_env['default_var2'] = "{% for val in _.var1 %}\n{{ val }}\n{% endfor %}\n"
option_1 = StrOption(name="var1", doc="a first variable", multi=True, default=["no", "yes", "maybe"], default_multi="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['jinja_to_function'], Params((), kwargs={'__internal_jinja': ParamValue("default_var2"), '__internal_type': ParamValue("string"), '__internal_multi': ParamValue(True), '__internal_files': ParamValue(['tests/dictionaries/00_2default_calculated_multi/dictionaries/rougail/00-base.yml']), '__internal_attribute': ParamValue("default"), '__internal_variable': ParamValue("var2"), '_.var1': ParamOption(option_1)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -0,0 +1,13 @@
---
version: 1.1
var1:
description: a first variable
multi: true
type: domainname
params:
allow_ip: true
var2:
description: a second variable
default:
type: variable
variable: _.var1

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": []
},
"rougail.var2": {
"owner": "default",
"value": []
}
}

View file

@ -0,0 +1,4 @@
{
"rougail.var1": [],
"rougail.var2": []
}

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": []
},
"rougail.var2": {
"owner": "default",
"value": []
}
}

View file

@ -0,0 +1 @@
["rougail.var1", "rougail.var2"]

View file

@ -0,0 +1,12 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_3 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_2)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2, option_3], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,17 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_4 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_3)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_7 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_8 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_7)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", children=[option_7, option_8], properties=frozenset({"basic"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = DomainnameOption(name="var1", doc="a first variable", multi=True, type="domainname", allow_ip=True, properties=frozenset({"basic", "mandatory"}), informations={'type': 'domainname'})
option_2 = DomainnameOption(name="var2", doc="a second variable", multi=True, default=Calculation(func['calc_value'], Params((ParamOption(option_1)))), type="domainname", allow_ip=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'domainname'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -0,0 +1,4 @@
---
version: '1.0'
var1:
description: a variable

View file

@ -0,0 +1,4 @@
---
version: "1.0"
var2:
description: a variable

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": null
},
"rougail.var2": {
"owner": "default",
"value": null
}
}

View file

@ -0,0 +1,4 @@
{
"rougail.var1": null,
"rougail.var2": null
}

View file

@ -0,0 +1,10 @@
{
"rougail.var1": {
"owner": "default",
"value": null
},
"rougail.var2": {
"owner": "default",
"value": null
}
}

View file

@ -0,0 +1 @@
["rougail.var1", "rougail.var2"]

View file

@ -0,0 +1,12 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_3 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2, option_3], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,17 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_4 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4], properties=frozenset({"basic"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"basic"}))
option_7 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_8 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", children=[option_7, option_8], properties=frozenset({"basic"}))
optiondescription_5 = OptionDescription(name="2", doc="2", children=[optiondescription_6], properties=frozenset({"basic"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_5])

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = StrOption(name="var1", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_2 = StrOption(name="var2", doc="a variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2])

View file

@ -0,0 +1,5 @@
---
version: '1.0'
without_type:
description: a variable
default: non

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.without_type": "non"
}

View file

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

View file

@ -0,0 +1,11 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,15 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
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 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
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])

View file

@ -0,0 +1,10 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = StrOption(name="without_type", doc="a variable", default="non", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])

View file

@ -0,0 +1,18 @@
---
version: '1.1'
var1: true # the first variable
var2:
description: the second variable
default: true
var3:
description: the third variable
type: boolean
default: true
var4: false # the forth variable
var5:
description: the fifth variable
default: false
var6:
description: the sixth variable
type: boolean
default: false

View file

@ -0,0 +1,26 @@
{
"rougail.var1": {
"owner": "default",
"value": true
},
"rougail.var2": {
"owner": "default",
"value": true
},
"rougail.var3": {
"owner": "default",
"value": true
},
"rougail.var4": {
"owner": "default",
"value": false
},
"rougail.var5": {
"owner": "default",
"value": false
},
"rougail.var6": {
"owner": "default",
"value": false
}
}

View file

@ -0,0 +1,8 @@
{
"rougail.var1": true,
"rougail.var2": true,
"rougail.var3": true,
"rougail.var4": false,
"rougail.var5": false,
"rougail.var6": false
}

View file

@ -0,0 +1,26 @@
{
"rougail.var1": {
"owner": "default",
"value": true
},
"rougail.var2": {
"owner": "default",
"value": true
},
"rougail.var3": {
"owner": "default",
"value": true
},
"rougail.var4": {
"owner": "default",
"value": false
},
"rougail.var5": {
"owner": "default",
"value": false
},
"rougail.var6": {
"owner": "default",
"value": false
}
}

View file

@ -0,0 +1,16 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_3 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_4 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_5 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_6 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_7 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", children=[option_2, option_3, option_4, option_5, option_6, option_7], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])

View file

@ -0,0 +1,25 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_4 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_5 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_6 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_7 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_8 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
optiondescription_2 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4, option_5, option_6, option_7, option_8], properties=frozenset({"standard"}))
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"standard"}))
option_11 = BoolOption(name="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_12 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_13 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_14 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_15 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_16 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
optiondescription_10 = OptionDescription(name="rougail", doc="Rougail", children=[option_11, option_12, option_13, option_14, option_15, option_16], properties=frozenset({"standard"}))
optiondescription_9 = OptionDescription(name="2", doc="2", children=[optiondescription_10], properties=frozenset({"standard"}))
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_9])

View file

@ -0,0 +1,15 @@
from tiramisu import *
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
from re import compile as re_compile
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_1 = BoolOption(name="var1", doc="the first variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_2 = BoolOption(name="var2", doc="the second variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_3 = BoolOption(name="var3", doc="the third variable", default=True, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_4 = BoolOption(name="var4", doc="the forth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_5 = BoolOption(name="var5", doc="the fifth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_6 = BoolOption(name="var6", doc="the sixth variable", default=False, properties=frozenset({"mandatory", "standard"}), informations={'type': 'boolean'})
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2, option_3, option_4, option_5, option_6])

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