diff --git a/src/rougail/annotator/fill.py b/src/rougail/annotator/fill.py index 017c5946a..5d9b328bd 100644 --- a/src/rougail/annotator/fill.py +++ b/src/rougail/annotator/fill.py @@ -32,7 +32,7 @@ from rougail.annotator.target import TargetAnnotator from rougail.annotator.param import ParamAnnotator -CALC_MULTI = ('calc_value', 'calc_list', 'get_range', 'calc_val_first_value', 'unbound_filename', 'zone_information', 'get_certificates', 'nsd_filename', 'get_linked_configuration') +CALC_MULTI = ('calc_value', 'calc_list', 'get_range', 'calc_val_first_value', 'unbound_filename', 'zone_information', 'get_certificates', 'nsd_filename', 'get_linked_configuration', 'get_internal_zones', 'nsd_concat_lists', 'get_internal_info_in_zone') class Annotator(TargetAnnotator, ParamAnnotator): diff --git a/src/rougail/annotator/param.py b/src/rougail/annotator/param.py index c316bb967..64e7a2e04 100644 --- a/src/rougail/annotator/param.py +++ b/src/rougail/annotator/param.py @@ -102,8 +102,11 @@ class ParamAnnotator: if param.type == 'suffix': for target in obj.target: if not self.objectspace.paths.variable_is_dynamic(target.name.path): + target_name = target.name + if isinstance(target_name, self.objectspace.variable): + target_name = target_name.name msg = _(f'"{param.type}" parameter cannot be set with target ' - f'"{target.name}" which is not a dynamic variable') + f'"{target_name}" which is not a dynamic variable') raise DictConsistencyError(msg, 53, obj.xmlfiles) elif param.type == 'index': for target in obj.target: diff --git a/src/rougail/convert.py b/src/rougail/convert.py index 3d2fe1d3b..87458c042 100644 --- a/src/rougail/convert.py +++ b/src/rougail/convert.py @@ -86,6 +86,7 @@ class RougailConvert: ) self.output = TiramisuReflector(rougailobjspace, functions_file, + rougailconfig['internal_functions'], ).get_text() + '\n' @staticmethod diff --git a/src/rougail/data/rougail.dtd b/src/rougail/data/rougail.dtd index 0829d5cf2..64a311640 100644 --- a/src/rougail/data/rougail.dtd +++ b/src/rougail/data/rougail.dtd @@ -90,7 +90,6 @@ - diff --git a/src/rougail/objspace.py b/src/rougail/objspace.py index 3be267288..ea48373c3 100644 --- a/src/rougail/objspace.py +++ b/src/rougail/objspace.py @@ -179,6 +179,7 @@ class RougailObjSpace: self.space, namespace, redefine_variables, + False, ) def _xml_parse(self, # pylint: disable=R0913 @@ -187,10 +188,15 @@ class RougailObjSpace: space, namespace, redefine_variables, + is_dynamic, ) -> None: # var to check unique family name in a XML file family_names = [] for child in document: + if is_dynamic: + is_sub_dynamic = True + else: + is_sub_dynamic = document.attrib.get('dynamic') is not None if not isinstance(child.tag, str): # doesn't proceed the XML commentaries continue @@ -225,6 +231,7 @@ class RougailObjSpace: document, variableobj, space, + is_sub_dynamic, ) self.add_to_tree_structure(variableobj, space, @@ -237,6 +244,7 @@ class RougailObjSpace: variableobj, namespace, redefine_variables, + is_sub_dynamic, ) def get_variableobj(self, @@ -472,6 +480,7 @@ class RougailObjSpace: document, variableobj, space, + is_dynamic, ): """Fill self.paths attributes """ @@ -488,7 +497,7 @@ class RougailObjSpace: self.paths.add_variable(namespace, variableobj.name, space.path, - document.attrib.get('dynamic') is not None, + is_dynamic, variableobj, leader, ) diff --git a/src/rougail/path.py b/src/rougail/path.py index a4a38762f..a9b984296 100644 --- a/src/rougail/path.py +++ b/src/rougail/path.py @@ -125,6 +125,8 @@ class Path: ) -> str: # pylint: disable=C0111 """Add a new variable (with path) """ + if name == 'hostname_' and not is_dynamic: + raise Exception('pffff') if '.' not in name: full_path = '.'.join([family, name]) if namespace == self.variable_namespace: diff --git a/src/rougail/template/base.py b/src/rougail/template/base.py index 19f12d39b..2f167743c 100644 --- a/src/rougail/template/base.py +++ b/src/rougail/template/base.py @@ -236,6 +236,7 @@ class RougailBaseTemplate: def patch_template(self, filename: str, + templates_dir: str, ) -> None: """Apply patch to a template """ @@ -251,7 +252,7 @@ class RougailBaseTemplate: msg = _(f"Error applying patch: '{patch_file}'\n" f"To reproduce and fix this error {patch_cmd_err}") self.log.error(_(msg)) - copy(join(self.templates_dir, filename), self.tmp_dir) + copy(join(templates_dir, filename), self.tmp_dir) def prepare_template(self, filename: str, @@ -263,7 +264,7 @@ class RougailBaseTemplate: if not isdir(self.tmp_dir): raise TemplateError(_(f'cannot find tmp_dir {self.tmp_dir}')) copy(join(templates_dir, filename), self.tmp_dir) - self.patch_template(filename) + self.patch_template(filename, templates_dir) def instance_file(self, filevar: Dict, diff --git a/src/rougail/template/systemd.py b/src/rougail/template/systemd.py index 338963e2c..61c6ce203 100644 --- a/src/rougail/template/systemd.py +++ b/src/rougail/template/systemd.py @@ -84,7 +84,7 @@ class RougailSystemdTemplate(RougailBaseTemplate): ) -> tuple: source = filevar['source'] if not isfile(source): # pragma: no cover - raise FileNotFound(_(f"File {source} does not exist.")) + raise FileNotFound(_(f'Source file "{source}" does not exist in {", ".join(self.templates_dir)}')) tmp_file = join(self.tmp_dir, source) #self.instance_file(fill, 'files') if variable: @@ -102,7 +102,7 @@ class RougailSystemdTemplate(RougailBaseTemplate): ) -> tuple: source = filevar['source'] if not isfile(source): # pragma: no cover - raise FileNotFound(_(f"File {source} does not exist.")) + raise FileNotFound(_(f'Override source file "{source}" does not exist in {", ".join(self.templates_dir)}')) tmp_file = join(self.tmp_dir, source) service_name = filevar['name'] destfile = f'/systemd/system/{service_name}.{service_type}.d/rougail.conf' diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py index 3ee27dcab..6e14ff7d5 100644 --- a/src/rougail/tiramisureflector.py +++ b/src/rougail/tiramisureflector.py @@ -32,12 +32,6 @@ from .annotator import CONVERT_OPTION from .objspace import RootRougailObject -class Root(): # pylint: disable=R0903 - """Root classes - """ - path = '.' - - class BaseElt: # pylint: disable=R0903 """Base element """ @@ -52,6 +46,7 @@ class TiramisuReflector: def __init__(self, objectspace, funcs_paths, + internal_functions, ): self.index = 0 self.text = [] @@ -73,6 +68,9 @@ class TiramisuReflector: " continue", " setattr(func, function, getattr(_func, function))", ]) + if internal_functions: + for func in internal_functions: + self.text.append(f"setattr(func, '{func}', {func})") self.text.extend(["try:", " from tiramisu3 import *", "except:", @@ -86,8 +84,11 @@ class TiramisuReflector: def make_tiramisu_objects(self) -> None: """make tiramisu objects """ + providers = {} baseelt = BaseElt() self.set_name(baseelt) + dynamic_path = '' + dynamic = False basefamily = Family(baseelt, self.text, self.objectspace, @@ -95,7 +96,12 @@ class TiramisuReflector: for elt in self.reorder_family(): self.populate_family(basefamily, elt, + providers, + dynamic, + dynamic_path, ) + basefamily.elt.information = providers + basefamily.populate_informations() self.baseelt = baseelt def reorder_family(self): @@ -115,6 +121,9 @@ class TiramisuReflector: def populate_family(self, parent_family, elt, + providers, + dynamic, + dynamic_path, ): """Populate family """ @@ -123,11 +132,21 @@ class TiramisuReflector: self.text, self.objectspace, ) + if not dynamic_path: + dynamic_path = elt.name + else: + dynamic_path = dynamic_path + '.' + elt.name + if dynamic or hasattr(elt, 'suffixes'): + dynamic_path += '{suffix}' + dynamic = True parent_family.add(family) for children in vars(elt).values(): if isinstance(children, self.objectspace.family): self.populate_family(family, children, + providers, + dynamic, + dynamic_path, ) continue if isinstance(children, dict): @@ -139,13 +158,21 @@ class TiramisuReflector: continue if isinstance(child, self.objectspace.variable): self.set_name(child) + sub_dynamic_path = dynamic_path + '.' + child.name + if dynamic: + sub_dynamic_path += '{suffix}' family.add(Variable(child, self.text, self.objectspace, + providers, + sub_dynamic_path, )) else: self.populate_family(family, child, + providers, + dynamic, + dynamic_path, ) def set_name(self, @@ -185,6 +212,8 @@ class Common: self.option_name = self.elt.reflector_name self.populate_attrib() self.populate_informations() + if hasattr(self.elt, 'provider'): + self.providers['provider:' + self.elt.provider] = self.dynamic_path return self.option_name def populate_attrib(self): @@ -239,7 +268,11 @@ class Common: """ if not hasattr(self.elt, 'information'): return - for key, value in vars(self.elt.information).items(): + if isinstance(self.elt.information, dict): + informations = self.elt.information + else: + informations = vars(self.elt.information) + for key, value in informations.items(): if key == 'xmlfiles': continue if isinstance(value, str): @@ -297,7 +330,11 @@ class Variable(Common): elt, text, objectspace, + providers, + dynamic_path, ): + self.providers = providers + self.dynamic_path = dynamic_path super().__init__(elt, text, objectspace) self.object_type = CONVERT_OPTION[elt.type]['opttype'] diff --git a/tests/dictionaries/01base_provider/00-base.xml b/tests/dictionaries/01base_provider/00-base.xml new file mode 100644 index 000000000..7f1f67693 --- /dev/null +++ b/tests/dictionaries/01base_provider/00-base.xml @@ -0,0 +1,13 @@ + + + + + + 0.527 + + + 0.527 + + + + diff --git a/tests/dictionaries/01base_provider/__init__.py b/tests/dictionaries/01base_provider/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/dictionaries/01base_provider/makedict/after.json b/tests/dictionaries/01base_provider/makedict/after.json new file mode 100644 index 000000000..005c64daf --- /dev/null +++ b/tests/dictionaries/01base_provider/makedict/after.json @@ -0,0 +1,12 @@ +{ + "rougail.general.float": { + "owner": "default", + "value": 0.527 + }, + "rougail.general.float_multi": { + "owner": "default", + "value": [ + 0.527 + ] + } +} diff --git a/tests/dictionaries/01base_provider/makedict/base.json b/tests/dictionaries/01base_provider/makedict/base.json new file mode 100644 index 000000000..99e81271b --- /dev/null +++ b/tests/dictionaries/01base_provider/makedict/base.json @@ -0,0 +1,6 @@ +{ + "rougail.general.float": 0.527, + "rougail.general.float_multi": [ + 0.527 + ] +} diff --git a/tests/dictionaries/01base_provider/makedict/before.json b/tests/dictionaries/01base_provider/makedict/before.json new file mode 100644 index 000000000..005c64daf --- /dev/null +++ b/tests/dictionaries/01base_provider/makedict/before.json @@ -0,0 +1,12 @@ +{ + "rougail.general.float": { + "owner": "default", + "value": 0.527 + }, + "rougail.general.float_multi": { + "owner": "default", + "value": [ + 0.527 + ] + } +} diff --git a/tests/dictionaries/01base_provider/tiramisu/base.py b/tests/dictionaries/01base_provider/tiramisu/base.py new file mode 100644 index 000000000..ba16b03e2 --- /dev/null +++ b/tests/dictionaries/01base_provider/tiramisu/base.py @@ -0,0 +1,22 @@ +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 +class func: + pass +_loader = _SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +_spec = _spec_from_loader(_loader.name, _loader) +_func = _module_from_spec(_spec) +_loader.exec_module(_func) +for function in dir(_func): + if function.startswith('_'): + continue + setattr(func, function, getattr(_func, function)) +try: + from tiramisu3 import * +except: + from tiramisu import * +option_3 = FloatOption(name="float", doc="Description", default=0.527, properties=frozenset({"mandatory", "normal"})) +option_4 = FloatOption(name="float_multi", doc="Description", multi=True, default=[0.527], default_multi=0.527, properties=frozenset({"mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="general", children=[option_3, option_4], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) +option_0.impl_set_information('provider:float', "rougail.general.float") diff --git a/tests/dictionaries/22provider_dynamic/00-base.xml b/tests/dictionaries/22provider_dynamic/00-base.xml new file mode 100644 index 000000000..a651a8fe5 --- /dev/null +++ b/tests/dictionaries/22provider_dynamic/00-base.xml @@ -0,0 +1,14 @@ + + + + + + val1 + val2 + + + + + + + diff --git a/tests/dictionaries/22provider_dynamic/__init__.py b/tests/dictionaries/22provider_dynamic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/dictionaries/22provider_dynamic/makedict/after.json b/tests/dictionaries/22provider_dynamic/makedict/after.json new file mode 100644 index 000000000..4201ce973 --- /dev/null +++ b/tests/dictionaries/22provider_dynamic/makedict/after.json @@ -0,0 +1,17 @@ +{ + "rougail.general.varname": { + "owner": "default", + "value": [ + "val1", + "val2" + ] + }, + "rougail.dynval1.vardynval1": { + "owner": "default", + "value": null + }, + "rougail.dynval2.vardynval2": { + "owner": "default", + "value": null + } +} diff --git a/tests/dictionaries/22provider_dynamic/makedict/base.json b/tests/dictionaries/22provider_dynamic/makedict/base.json new file mode 100644 index 000000000..f3dfb5918 --- /dev/null +++ b/tests/dictionaries/22provider_dynamic/makedict/base.json @@ -0,0 +1,8 @@ +{ + "rougail.general.varname": [ + "val1", + "val2" + ], + "rougail.dynval1.vardynval1": null, + "rougail.dynval2.vardynval2": null +} diff --git a/tests/dictionaries/22provider_dynamic/makedict/before.json b/tests/dictionaries/22provider_dynamic/makedict/before.json new file mode 100644 index 000000000..4201ce973 --- /dev/null +++ b/tests/dictionaries/22provider_dynamic/makedict/before.json @@ -0,0 +1,17 @@ +{ + "rougail.general.varname": { + "owner": "default", + "value": [ + "val1", + "val2" + ] + }, + "rougail.dynval1.vardynval1": { + "owner": "default", + "value": null + }, + "rougail.dynval2.vardynval2": { + "owner": "default", + "value": null + } +} diff --git a/tests/dictionaries/22provider_dynamic/tiramisu/base.py b/tests/dictionaries/22provider_dynamic/tiramisu/base.py new file mode 100644 index 000000000..3f9b08ac8 --- /dev/null +++ b/tests/dictionaries/22provider_dynamic/tiramisu/base.py @@ -0,0 +1,24 @@ +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 +class func: + pass +_loader = _SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py') +_spec = _spec_from_loader(_loader.name, _loader) +_func = _module_from_spec(_spec) +_loader.exec_module(_func) +for function in dir(_func): + if function.startswith('_'): + continue + setattr(func, function, getattr(_func, function)) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = StrOption(name="varname", doc="No change", multi=True, default=['val1', 'val2'], default_multi="val1", properties=frozenset({"mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="general", children=[option_3], properties=frozenset({"normal"})) +option_5 = StrOption(name="vardyn", doc="No change", properties=frozenset({"normal"})) +option_4 = ConvertDynOptionDescription(name="dyn", doc="dyn", suffixes=Calculation(func.calc_value, Params((ParamOption(option_3, notraisepropertyerror=True)))), children=[option_5], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_4]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) +option_0.impl_set_information('provider:dyn', "rougail.dyn{suffix}.vardyn{suffix}")