diff --git a/locale/fr/LC_MESSAGES/rougail.po b/locale/fr/LC_MESSAGES/rougail.po index a5a15c4aa..318156a5b 100644 --- a/locale/fr/LC_MESSAGES/rougail.po +++ b/locale/fr/LC_MESSAGES/rougail.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2024-11-25 09:10+0100\n" -"PO-Revision-Date: 2024-11-25 09:11+0100\n" +"POT-Creation-Date: 2024-12-15 16:50+0100\n" +"PO-Revision-Date: 2024-12-15 16:55+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: fr\n" @@ -16,31 +16,31 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.5\n" -#: src/rougail/annotator/family.py:141 +#: src/rougail/annotator/family.py:143 msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}" msgstr "" "le mode d'une variable par défaut \"{0}\" n'est pas un mode valide, les " "modes valides sont {1}" -#: src/rougail/annotator/family.py:147 +#: src/rougail/annotator/family.py:149 msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}" msgstr "" "le mode d'une famille par défaut \"{0}\" n'est pas un mode valide, les modes " "valides sont {1}" -#: src/rougail/annotator/family.py:179 +#: src/rougail/annotator/family.py:181 msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}" msgstr "" "le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, les modes valides " "sont {2}" -#: src/rougail/annotator/family.py:183 +#: src/rougail/annotator/family.py:185 msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available" msgstr "" "le mode \"{0}\" pour \"{1}\" n'est pas un mode valide, aucun mode ne sont " "définis" -#: src/rougail/annotator/family.py:247 +#: src/rougail/annotator/family.py:249 msgid "" "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the " "higher family mode \"{2}\"" @@ -48,7 +48,7 @@ msgstr "" "la variable \"{0}\" est obligatoire donc dans le mode \"{1}\" mais la " "famille a un mode supérieur \"{2}\"" -#: src/rougail/annotator/family.py:285 +#: src/rougail/annotator/family.py:287 msgid "" "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode " "\"{2}\"" @@ -56,7 +56,7 @@ msgstr "" "la variable suiveuse \"{0}\" a le mode \"{1}\" mais la variable leader a un " "mode supérieur \"{2}\"" -#: src/rougail/annotator/family.py:318 +#: src/rougail/annotator/family.py:320 msgid "" "the family \"{0}\" is in \"{1}\" mode but variables and families inside have " "the higher modes \"{2}\"" @@ -64,7 +64,7 @@ msgstr "" "la famille \"{0}\" a le mode \"{1}\" mais les variables et les familles à " "l'intérieur ont des modes supérieurs \"{2}\"" -#: src/rougail/annotator/family.py:336 +#: src/rougail/annotator/family.py:338 msgid "" "the variable \"{0}\" is in \"{1}\" mode but family has the higher family " "mode \"{2}\"" @@ -102,6 +102,64 @@ msgid "" msgstr "" "la variable \"{0}\" a la valeur par défaut invalide \"{1}\" devrait être {2}" +#: src/rougail/config.py:216 +msgid "Directories where dictionary files are placed" +msgstr "Répertoires où sont placés les fichiers de structure" + +#: src/rougail/config.py:227 +msgid "Sort dictionaries from differents directories" +msgstr "Trier les fichiers de structure à partir de différents répertoires" + +#: src/rougail/config.py:232 +msgid "Main namespace name" +msgstr "Nom de l'espace de nom principal" + +#: src/rougail/config.py:238 +msgid "Extra namespaces" +msgstr "Espaces de nom supplémentaires" + +#: src/rougail/config.py:245 +msgid "Extra namespace name" +msgstr "Nom de l'espace de nom supplémentaire" + +#: src/rougail/config.py:251 +msgid "Directories where extra dictionary files are placed" +msgstr "" +"Répertoires où sont placés les fichiers de structure de l'espace de nom " +"supplémentaire" + +#: src/rougail/config.py:262 +msgid "Update dictionaries to newest Rougail format version" +msgstr "" +"Mettre à jour le fichier de structure vers la dernière version du format de " +"Rougail" + +#: src/rougail/config.py:263 +msgid "Do not update dictionaries to newest Rougail format version" +msgstr "" +"Ne pas mettre à jour le fichier de structure vers la dernière version du " +"format de Rougail" + +#: src/rougail/config.py:267 +msgid "Update informations" +msgstr "Mise à jour des informations" + +#: src/rougail/config.py:273 +msgid "Directories where dictionary files will be placed" +msgstr "Répertoires où sont placés les fichiers de structure" + +#: src/rougail/config.py:278 +msgid "Directories where extra files will be placed" +msgstr "Répertoires où sont placés les fichiers de structure supplémentaire" + +#: src/rougail/config.py:290 +msgid "File with functions" +msgstr "Fichier avec les fonctions" + +#: src/rougail/config.py:302 +msgid "All modes level available" +msgstr "Tous les niveaux de modes valides" + #: src/rougail/convert.py:281 msgid "" "A variable or a family located in the \"{0}\" namespace shall not be used in " @@ -110,15 +168,19 @@ msgstr "" "Une variable ou une famille localisé dans l'espace de nom \"{0}\" ne devrait " "pas être utilisé dans l'espace de nom \"{1}\"" -#: src/rougail/convert.py:477 +#: src/rougail/convert.py:480 msgid "unknown type {0} for {1}" msgstr "type {0} inconnu pour {1}" -#: src/rougail/convert.py:1347 +#: src/rougail/convert.py:613 +msgid "The family \"{0}\" already exists and it is not redefined" +msgstr "La famille \"{0}\" existe déjà et n'est pas redéfinie" + +#: src/rougail/convert.py:1356 msgid "duplicate dictionary file name {0}" msgstr "nom de fichier {0} de dictionnaire dupliqué" -#: src/rougail/convert.py:1394 +#: src/rougail/convert.py:1403 msgid "Cannot execute annotate multiple time" msgstr "Ne peut exécuter l'annotation plusieurs fois" diff --git a/locale/rougail.pot b/locale/rougail.pot index 6af0904a5..1770d0914 100644 --- a/locale/rougail.pot +++ b/locale/rougail.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2024-11-25 09:10+0100\n" +"POT-Creation-Date: 2024-12-15 16:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,35 +15,35 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: src/rougail/annotator/family.py:141 +#: src/rougail/annotator/family.py:143 msgid "default variable mode \"{0}\" is not a valid mode, valid modes are {1}" msgstr "" -#: src/rougail/annotator/family.py:147 +#: src/rougail/annotator/family.py:149 msgid "default family mode \"{0}\" is not a valid mode, valid modes are {1}" msgstr "" -#: src/rougail/annotator/family.py:179 +#: src/rougail/annotator/family.py:181 msgid "mode \"{0}\" for \"{1}\" is not a valid mode, valid modes are {2}" msgstr "" -#: src/rougail/annotator/family.py:183 +#: src/rougail/annotator/family.py:185 msgid "mode \"{0}\" for \"{1}\" is not a valid mode, no modes are available" msgstr "" -#: src/rougail/annotator/family.py:247 +#: src/rougail/annotator/family.py:249 msgid "the variable \"{0}\" is mandatory so in \"{1}\" mode but family has the higher family mode \"{2}\"" msgstr "" -#: src/rougail/annotator/family.py:285 +#: src/rougail/annotator/family.py:287 msgid "the follower \"{0}\" is in \"{1}\" mode but leader have the higher mode \"{2}\"" msgstr "" -#: src/rougail/annotator/family.py:318 +#: src/rougail/annotator/family.py:320 msgid "the family \"{0}\" is in \"{1}\" mode but variables and families inside have the higher modes \"{2}\"" msgstr "" -#: src/rougail/annotator/family.py:336 +#: src/rougail/annotator/family.py:338 msgid "the variable \"{0}\" is in \"{1}\" mode but family has the higher family mode \"{2}\"" msgstr "" @@ -67,19 +67,75 @@ msgstr "" msgid "the variable \"{0}\" has an unvalid default value \"{1}\" should be in {2}" msgstr "" +#: src/rougail/config.py:216 +msgid "Directories where dictionary files are placed" +msgstr "" + +#: src/rougail/config.py:227 +msgid "Sort dictionaries from differents directories" +msgstr "" + +#: src/rougail/config.py:232 +msgid "Main namespace name" +msgstr "" + +#: src/rougail/config.py:238 +msgid "Extra namespaces" +msgstr "" + +#: src/rougail/config.py:245 +msgid "Extra namespace name" +msgstr "" + +#: src/rougail/config.py:251 +msgid "Directories where extra dictionary files are placed" +msgstr "" + +#: src/rougail/config.py:262 +msgid "Update dictionaries to newest Rougail format version" +msgstr "" + +#: src/rougail/config.py:263 +msgid "Do not update dictionaries to newest Rougail format version" +msgstr "" + +#: src/rougail/config.py:267 +msgid "Update informations" +msgstr "" + +#: src/rougail/config.py:273 +msgid "Directories where dictionary files will be placed" +msgstr "" + +#: src/rougail/config.py:278 +msgid "Directories where extra files will be placed" +msgstr "" + +#: src/rougail/config.py:290 +msgid "File with functions" +msgstr "" + +#: src/rougail/config.py:302 +msgid "All modes level available" +msgstr "" + #: src/rougail/convert.py:281 msgid "A variable or a family located in the \"{0}\" namespace shall not be used in the \"{1}\" namespace" msgstr "" -#: src/rougail/convert.py:477 +#: src/rougail/convert.py:480 msgid "unknown type {0} for {1}" msgstr "" -#: src/rougail/convert.py:1347 +#: src/rougail/convert.py:613 +msgid "The family \"{0}\" already exists and it is not redefined" +msgstr "" + +#: src/rougail/convert.py:1356 msgid "duplicate dictionary file name {0}" msgstr "" -#: src/rougail/convert.py:1394 +#: src/rougail/convert.py:1403 msgid "Cannot execute annotate multiple time" msgstr "" diff --git a/src/rougail/annotator/__init__.py b/src/rougail/annotator/__init__.py index ef00879b3..9111edaa9 100644 --- a/src/rougail/annotator/__init__.py +++ b/src/rougail/annotator/__init__.py @@ -71,30 +71,38 @@ class SpaceAnnotator: # pylint: disable=R0903 if extra_annotator in ANNOTATORS: continue get_annotators(ANNOTATORS, extra_annotator) - if objectspace.output: + for structural in objectspace.structurals: try: - get_annotators(ANNOTATORS, f"rougail.output_{objectspace.output}", "annotator") + get_annotators( + ANNOTATORS, f"rougail.structural_{structural}", "annotator" + ) except ModuleNotFoundError: pass for user_data in objectspace.user_datas: try: - get_annotators(ANNOTATORS, f"rougail.user_data_{user_data}", "annotator") + get_annotators( + ANNOTATORS, f"rougail.user_data_{user_data}", "annotator" + ) except ModuleNotFoundError: pass - for plugin in objectspace.plugins: + if objectspace.output: try: - get_annotators(ANNOTATORS, f"rougail.{plugin}", "annotator") + get_annotators( + ANNOTATORS, f"rougail.output_{objectspace.output}", "annotator" + ) except ModuleNotFoundError: pass annotators = ANNOTATORS["rougail.annotator"].copy() for extra_annotator in objectspace.extra_annotators: annotators.extend(ANNOTATORS[extra_annotator]) - for plugin in objectspace.plugins: - annotators.extend(ANNOTATORS[f"rougail.{plugin}.annotator"]) + for structural in objectspace.structurals: + annotators.extend(ANNOTATORS[f"rougail.structural_{structural}.annotator"]) for user_data in objectspace.user_datas: annotators.extend(ANNOTATORS[f"rougail.user_data_{user_data}.annotator"]) if objectspace.output: - annotators.extend(ANNOTATORS[f"rougail.output_{objectspace.output}.annotator"]) + annotators.extend( + ANNOTATORS[f"rougail.output_{objectspace.output}.annotator"] + ) annotators = sorted(annotators, key=get_level) functions = {} functions_files = objectspace.functions_files diff --git a/src/rougail/annotator/family.py b/src/rougail/annotator/family.py index fd0cd3be8..df2813438 100644 --- a/src/rougail/annotator/family.py +++ b/src/rougail/annotator/family.py @@ -126,12 +126,15 @@ class Annotator(Walk): family.dynamic, VariableCalculation ): path = family.dynamic.variable - if family.version != "1.0" and self.objectspace.paths.regexp_relative.search(path): + if ( + family.version != "1.0" + and self.objectspace.paths.regexp_relative.search(path) + ): path = self.objectspace.paths.get_full_path( family.dynamic.variable, family.path, ) - if family.version == '1.0' and "{{ suffix }}" in path: + if family.version == "1.0" and "{{ suffix }}" in path: path = path.replace("{{ suffix }}", "{{ identifier }}") self.objectspace.informations.add(family.path, "dynamic_variable", path) diff --git a/src/rougail/config.py b/src/rougail/config.py index dd17bd911..025c2fa75 100644 --- a/src/rougail/config.py +++ b/src/rougail/config.py @@ -64,7 +64,11 @@ def get_sub_modules(): def get_level(module): - return module["level"] + return float(module["level"]) + { + "structural": 0.1, + "user data": 0.2, + "output": 0.3, + }.get(module["process"]) class _RougailConfig: @@ -187,10 +191,12 @@ class FakeRougailConvert(RougailConvert): self.export_with_import = True self.internal_functions = [] self.force_optional = False - self.plugins = ["structural_commandline"] + self.structurals = ["commandline"] self.user_datas = [] self.output = None self.add_extra_options = self.add_extra_options + self.tiramisu_cache = False + self.load_unexist_redefine = False def get_rougail_config( @@ -198,10 +204,6 @@ 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 = f"""default_dictionary_format_version: description: Dictionary format version by default, if not specified in dictionary file alternative_name: v @@ -210,82 +212,8 @@ def get_rougail_config( - '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_namespace_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 + description: {_("File with functions")} alternative_name: c type: unix_filename params: @@ -297,74 +225,73 @@ functions_files: mandatory: false modes_level: - description: All modes level available + description: {_("All modes level available")} multi: true mandatory: false """ if backward_compatibility: - rougail_options += """ - default: + rougail_options += """ default: - basic - standard - advanced """ - rougail_options += """ + rougail_options += f""" default_family_mode: - description: Default mode for a family + description: {_("Default mode for a family")} default: jinja: | - {% if modes_level %} - {{ modes_level[0] }} - {% endif %} + {{% if modes_level %}} + {{{{ modes_level[0] }}}} + {{% endif %}} disabled: jinja: | - {% if not modes_level %} + {{% if not modes_level %}} No mode - {% endif %} + {{% endif %}} validators: - type: jinja jinja: | - {% if default_family_mode not in modes_level %} - not in modes_level ({modes_level}) - {% endif %} + {{% 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 + description: {_("Default mode for a variable")} default: jinja: | - {% if modes_level %} - {% if modes_level | length == 1 %} - {{ modes_level[0] }} - {% else %} - {{ modes_level[1] }} - {% endif %} - {% endif %} + {{% if modes_level %}} + {{% if modes_level | length == 1 %}} + {{{{ modes_level[0] }}}} + {{% else %}} + {{{{ modes_level[1] }}}} + {{% endif %}} + {{% endif %}} disabled: jinja: | - {% if not modes_level %} + {{% if not modes_level %}} No mode - {% endif %} + {{% endif %}} validators: - type: jinja jinja: | - {% if default_variable_mode not in modes_level %} - not in modes_level ({modes_level}) - {% endif %} + {{% 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 + 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 + description: {_("In cache file, do not importation of Tiramisu and other dependencies")} default: false commandline: false tiramisu_cache: - description: Tiramisu cache filename + description: {_("Tiramisu cache filename")} alternative_name: t type: unix_filename mandatory: false @@ -372,52 +299,52 @@ tiramisu_cache: allow_relative: true internal_functions: - description: Name of internal functions that we can use as a function + 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 + description: {_("Name of extra annotators")} multi: true mandatory: false commandline: false suffix: - description: Suffix add to generated option name + description: {_("Suffix add to generated option name")} default: '' mandatory: false commandline: false force_optional: - description: Every variable in calculation are optional - negative_description: Variable in calculation are not optional by default + description: {_("Every variable in calculation are optional")} + negative_description: {_("Variable in calculation are not optional by default")} default: False + +load_unexist_redefine: + description: {_("Load redefine variable even if there don't already exists")} + negative_description: {_("Load redefine variable even if there don't already exists")} + commandline: false + default: False + """ processes = { "structural": [], - "output": [], "user data": [], + "output": [], } for module in get_sub_modules().values(): - data = module.get_rougail_config() + data = module.get_rougail_config(backward_compatibility=backward_compatibility) 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""" + rougail_process = "step: # Load and exporter steps" for process in processes: if processes[process]: objects = processes[process] rougail_process += """ + {NAME}: description: Select for {NAME} alternative_name: {NAME[0]} @@ -428,26 +355,29 @@ force_optional: for obj in objects: rougail_process += f" - {obj['name']}\n" if process == "structural": - rougail_process += " commandline: false" + rougail_process += """ commandline: false + multi: true + default: + - directory +""" elif process == "user data": rougail_process += """ multi: true - mandatory: false -""" + 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: + 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( + {% endif %}""".replace( "NAME", hidden_output ) elif objects: @@ -455,10 +385,10 @@ force_optional: DEFAULT=objects[0]["name"] ) else: - if process == 'output': - prop = 'hidden' + if process == "output": + prop = "hidden" else: - prop = 'disabled' + prop = "disabled" rougail_process += """ {NAME}: description: Select for {NAME} @@ -473,8 +403,9 @@ force_optional: PROP=prop, ) rougail_options += rougail_process + # print(rougail_options) convert = FakeRougailConvert(add_extra_options) - convert._init() + convert.init() convert.namespace = None convert.parse_root_file( "rougail.config", @@ -483,25 +414,27 @@ force_optional: 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 - if not isinstance(obj["options"], list): - options = [obj["options"]] - else: - options = obj["options"] - for option in options: - convert.parse_root_file( - f'rougail.config.{obj["name"]}', - "", - "1.1", - YAML().load(option), - ) + objects = [] + for obj in sorted( + [obj for objects in processes.values() for obj in objects], key=get_level + ): + if "extra_vars" in obj: + extra_vars |= obj["extra_vars"] + if not "options" in obj: + continue + if not isinstance(obj["options"], list): + options = [obj["options"]] + else: + options = obj["options"] + for option in options: + convert.parse_root_file( + f'rougail.config.{obj["name"]}', + "", + "1.1", + YAML().load(option), + ) - tiram_obj = convert.save(None) + tiram_obj = convert.save() optiondescription = {} exec(tiram_obj, {}, optiondescription) # pylint: disable=W0122 return _RougailConfig( diff --git a/src/rougail/error.py b/src/rougail/error.py index 5d4921eab..9cb99aadb 100644 --- a/src/rougail/error.py +++ b/src/rougail/error.py @@ -69,10 +69,6 @@ class DictConsistencyError(Exception): self.errno = errno -class UpgradeError(Exception): - """Error during XML upgrade""" - - ## ---- generic exceptions ---- diff --git a/src/rougail/locale/fr/LC_MESSAGES/rougail.mo b/src/rougail/locale/fr/LC_MESSAGES/rougail.mo index 9cf90c69d..25b24c22f 100644 Binary files a/src/rougail/locale/fr/LC_MESSAGES/rougail.mo and b/src/rougail/locale/fr/LC_MESSAGES/rougail.mo differ diff --git a/src/rougail/object_model.py b/src/rougail/object_model.py index f2eb4b9c4..995fba0bd 100644 --- a/src/rougail/object_model.py +++ b/src/rougail/object_model.py @@ -27,7 +27,7 @@ from pydantic import ( ConfigDict, ) from tiramisu import undefined -from .utils import get_jinja_variable_to_param, get_realpath +from .utils import get_jinja_variable_to_param from .error import DictConsistencyError, VariableCalculationDependencyError BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None] @@ -178,7 +178,6 @@ PARAM_TYPES = { class Calculation(BaseModel): - path_prefix: Optional[str] path: str inside_list: bool version: str @@ -189,12 +188,6 @@ class Calculation(BaseModel): model_config = ConfigDict(extra="forbid") - def get_realpath( - self, - path: str, - ) -> str: - return get_realpath(path, self.path_prefix) - def get_params(self, objectspace): if not self.params: return {} @@ -208,7 +201,6 @@ class Calculation(BaseModel): path = self.ori_path variable, identifier = objectspace.paths.get_with_dynamic( param["variable"], - self.path_prefix, path, self.version, self.namespace, @@ -220,7 +212,12 @@ class Calculation(BaseModel): raise DictConsistencyError(msg, 22, self.xmlfiles) continue if not isinstance(variable, objectspace.variable): - raise Exception("pfff it's a family") + if isinstance(variable, objectspace.family): + msg = f'the variable "{variable["name"]}" is in fact a family in attribute "{self.attribute_name}" for "{self.path}"' + raise DictConsistencyError(msg, 42, self.xmlfiles) + else: + msg = f'unknown object "{variable}" in attribute "{self.attribute_name}" for "{self.path}"' + raise DictConsistencyError(msg, 44, self.xmlfiles) param["variable"] = variable if identifier: param["identifier"] = identifier @@ -232,7 +229,6 @@ class Calculation(BaseModel): path = self.ori_path variable, identifier = objectspace.paths.get_with_dynamic( param["variable"], - self.path_prefix, path, self.version, self.namespace, @@ -314,7 +310,6 @@ class JinjaCalculation(Calculation): objectspace, variable.xmlfiles, objectspace.functions, - self.path_prefix, self.version, self.namespace, ): @@ -413,15 +408,18 @@ class _VariableCalculation(Calculation): path = self.ori_path variable, identifier = 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") + if isinstance(variable, objectspace.family): + msg = f'the variable "{variable.path}" is in fact a family in attribute "{self.attribute_name}" for "{self.path}"' + raise DictConsistencyError(msg, 47, self.xmlfiles) + else: + msg = f'unknown object "{variable}" in attribute "{self.attribute_name}" for "{self.path}"' + raise DictConsistencyError(msg, 48, self.xmlfiles) return variable, identifier def get_params( @@ -434,9 +432,13 @@ class _VariableCalculation(Calculation): ): if not variable: if not objectspace.force_optional: - msg = f'Variable not found "{self.variable}" for attribut "{self.attribute_name}" for variable "{self.path}"' + if self.ori_path is None: + path = self.path + else: + path = self.ori_path + msg = f'Variable not found "{self.variable}" for attribut "{self.attribute_name}" in variable "{path}"' raise DictConsistencyError(msg, 88, self.xmlfiles) - return {None: [['example']]} + return {None: [["example"]]} param = { "type": "variable", "variable": variable, @@ -497,7 +499,7 @@ class _VariableCalculation(Calculation): multi = objectspace.multis[self.path] == "submulti" else: multi = self.path in objectspace.multis - if multi: + if multi and not self.inside_list: params["__internal_multi"] = True return params @@ -514,7 +516,11 @@ class VariableCalculation(_VariableCalculation): msg = f'"{self.attribute_name}" variable shall not have an "optional" attribute for variable "{self.variable}"' raise DictConsistencyError(msg, 33, self.xmlfiles) variable, identifier = self.get_variable(objectspace) - if not variable and self.optional or (objectspace.force_optional and self.attribute_name == "default"): + if ( + not variable + and self.optional + or (objectspace.force_optional and self.attribute_name == "default") + ): raise VariableCalculationDependencyError() params = self.get_params( objectspace, @@ -600,7 +606,6 @@ class InformationCalculation(Calculation): path = self.ori_path variable, identifier = objectspace.paths.get_with_dynamic( self.variable, - self.path_prefix, path, self.version, self.namespace, @@ -704,16 +709,16 @@ class IndexCalculation(Calculation): CALCULATION_TYPES = { "jinja": JinjaCalculation, - "variable": VariableCalculation, "information": InformationCalculation, + "variable": VariableCalculation, "identifier": IdentifierCalculation, "suffix": IdentifierCalculation, "index": IndexCalculation, } CALCULATION_PROPERTY_TYPES = { "jinja": JinjaCalculation, - "variable": VariablePropertyCalculation, "information": InformationCalculation, + "variable": VariablePropertyCalculation, "identifier": IdentifierPropertyCalculation, "index": IndexCalculation, } @@ -722,14 +727,18 @@ BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, Calculation class Family(BaseModel): name: str + # informations description: Optional[str] = None - type: Literal["family", "leadership", "dynamic"] = "family" - path: str help: Optional[str] = None mode: Optional[str] = None + # validation + type: Literal["family", "leadership", "dynamic"] = "family" + # properties hidden: Union[bool, Calculation] = False disabled: Union[bool, Calculation] = False + # others namespace: Optional[str] + path: str version: str xmlfiles: List[str] = [] @@ -743,30 +752,34 @@ class Dynamic(Family): class Variable(BaseModel): - # type will be set dynamically in `annotator/value.py`, default is None - type: str = None name: str + # user informations 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 - unique: Optional[bool] = None help: Optional[str] = None - hidden: Union[bool, Calculation] = False - disabled: Union[bool, Calculation] = False + mode: Optional[str] = None + examples: Optional[list] = None + test: Optional[list] = None + # validations + ## type will be set dynamically in `annotator/value.py`, default is None + type: str = None + params: Optional[List[Param]] = None + regexp: Optional[str] = None + choices: Optional[Union[List[BASETYPE_CALC], Calculation]] = None + multi: Optional[bool] = None + validators: Optional[List[Calculation]] = None + # value + default: Union[List[BASETYPE_CALC], BASETYPE_CALC] = None + # properties + auto_save: bool = False mandatory: Union[None, bool, Calculation] = None empty: Union[None, bool, Calculation] = True - auto_save: bool = False - mode: Optional[str] = None - test: Optional[list] = None - examples: Optional[list] = None + unique: Optional[bool] = None + hidden: Union[bool, Calculation] = False + disabled: Union[bool, Calculation] = False + # others path: str namespace: Optional[str] version: str - path_prefix: Optional[str] xmlfiles: List[str] = [] model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) @@ -779,7 +792,6 @@ class SymLink(BaseModel): opt: Variable namespace: Optional[str] version: str - path_prefix: Optional[str] xmlfiles: List[str] = [] model_config = ConfigDict(extra="forbid") diff --git a/src/rougail/structural_commandline/__init__.py b/src/rougail/structural_commandline/__init__.py index e69de29bb..d4a05accb 100644 --- a/src/rougail/structural_commandline/__init__.py +++ b/src/rougail/structural_commandline/__init__.py @@ -0,0 +1,22 @@ +""" +Silique (https://www.silique.fr) +Copyright (C) 2024 + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by the +Free Software Foundation, either version 3 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 Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +""" + +from .object_model import Variable, Family + + +__all__ = ("Variable", "Family") diff --git a/src/rougail/structural_commandline/config.py b/src/rougail/structural_commandline/config.py index 903f8817e..3deef437b 100644 --- a/src/rougail/structural_commandline/config.py +++ b/src/rougail/structural_commandline/config.py @@ -27,12 +27,13 @@ def get_rougail_config( 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", + "name": "cmdline", "process": "structural", "options": options, "level": 20, diff --git a/src/rougail/structural_commandline/object_model.py b/src/rougail/structural_commandline/object_model.py index cdb93e3cc..0de5bac64 100644 --- a/src/rougail/structural_commandline/object_model.py +++ b/src/rougail/structural_commandline/object_model.py @@ -29,6 +29,3 @@ class Variable(BaseModel): class Family(BaseModel): commandline: bool = True - - -__all__ = ("Variable", "Family") diff --git a/src/rougail/tiramisu.py b/src/rougail/tiramisu.py index a48064f0f..9105b9643 100644 --- a/src/rougail/tiramisu.py +++ b/src/rougail/tiramisu.py @@ -127,7 +127,9 @@ def jinja_to_function( c_kw = kw path, var = key.rsplit(".", 1) if isinstance(value, CancelParam): - count_o_path = value.origin_path.count('.') - value.current_path.count('.') + count_o_path = value.origin_path.count(".") - value.current_path.count( + "." + ) path = path.rsplit(".", count_o_path)[0] for subkey in path.split("."): c_kw = c_kw.setdefault(subkey, {}) diff --git a/src/rougail/tiramisureflector.py b/src/rougail/tiramisureflector.py index 0766b53c1..6fa6b8803 100644 --- a/src/rougail/tiramisureflector.py +++ b/src/rougail/tiramisureflector.py @@ -87,7 +87,7 @@ class TiramisuReflector: continue self.text["header"].append(f"load_functions('{funcs_path}')") if self.objectspace.export_with_import: - if objectspace.main_namespace: + if self.objectspace.has_namespace: self.text["header"].extend( [ "try:", @@ -372,6 +372,11 @@ class Common: ) params = [f"{option_name}"] if identifier is not None: + if not self.objectspace.paths.is_dynamic(variable.path): + msg = _("internal error, {0} is not a dynamic variable").format( + variable.path + ) + raise DictConsistencyError(msg, 49, self.elt.xmlfiles) param_type = "ParamDynOption" identifiers = [] for ident in identifier: diff --git a/src/rougail/update/update.py b/src/rougail/update/update.py index 67bf24be2..e69de29bb 100644 --- a/src/rougail/update/update.py +++ b/src/rougail/update/update.py @@ -1,1330 +0,0 @@ -"""Update Rougail structure file to new version - -Cadoles (http://www.cadoles.com) -Copyright (C) 2021 - -Silique (https://www.silique.fr) -Copyright (C) 2022-2024 - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU Lesser General Public License as published by the -Free Software Foundation, either version 3 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 Lesser General Public License for more -details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -""" - -from os import listdir -from os.path import basename, isdir, isfile, join -from typing import Any, List, Optional, Tuple - -try: - from lxml.etree import SubElement # pylint: disable=E0611 - from lxml.etree import Element, XMLParser, XMLSyntaxError, parse, tostring -except ModuleNotFoundError as err: - parse = None - -# from ast import parse as ast_parse -from json import dumps -from ruamel.yaml import YAML -from pathlib import Path - -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"] - - -def get_function_name(version): - version = version.replace(".", "_") - return f"update_{version}" - - -FUNCTION_VERSIONS = [(version, get_function_name(version)) for version in VERSIONS] - - -class upgrade_010_to_10: - def __init__( - self, - dico: dict, - namespace: str, - xmlsrc: str, - ) -> None: - self.xmlsrc = xmlsrc - self.paths = {"family": {}, "variable": {}, "dynamic": {}} - self.lists = { - "service": {}, - "ip": {}, - "certificate": {}, - "file": {}, - } - self.flatten_paths = {"family": {}, "variable": {}} - self.variables = self.parse_variables(dico, namespace, namespace, root=True) - self.parse_variables_with_path() - self.parse_services(dico) - self.parse_constraints(dico) - - def parse_variables( - 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: - 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 - - def convert_family( - self, - 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: - sub_path = name - # leadership and dynamic are no more attribute, it's now a type - for typ in ["leadership", "dynamic"]: - if typ in family: - value = family.pop(typ) - if value: - family["type"] = typ - if typ == "dynamic": - family["variable"] = self.get_variable_path(value) - # add sub families and sub variables - 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 - family[f"_{sub_name}"] = family.pop(sub_name) - # store converted family - family.update(sub_families) - new_families[name] = family - self.flatten_paths["family"][name] = sub_path - self.paths["family"][sub_path] = family - - def convert_variable( - self, - 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 - if ( - "redefine" not in variable - and "value" not in variable - and "mandatory" not in variable - and ("type" not in variable or variable["type"] != "boolean") - ): - variable["mandatory"] = False - if "remove_condition" in variable and variable.pop("remove_condition"): - for prop in ["hidden", "disabled", "mandatory"]: - if prop not in variable: - variable[prop] = False - if "remove_choice" in variable: - if "choice" not in variable: - variable["choice"] = None - variable.pop("remove_choice") - if "remove_check" in variable: - variable.pop("remove_check") - if "validators" not in variable: - variable["validators"] = None - if "remove_fill" in variable: - variable.pop("remove_fill") - variable["value"] = None - if "auto_freeze" in variable: - variable.pop("auto_freeze") - if "test" in variable: - tests = [] - for test in variable["test"].split("|"): - if test == "": - tests.append(None) - else: - tests.append( - CONVERT_OPTION.get(variable.get("type", "string"), {}).get( - "func", str - )(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 multi and len(default) == 1: - variable["default"] = self.get_value(default[0], multi) - else: - variable["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, multi) - for choice in variable.pop("choice") - ] - - def parse_services( - self, - dico: dict, - ) -> None: - self.services = {} - if "services" in dico: - for root_services in dico["services"]: - for services in root_services.values(): - for service in services: - new_service = {} - for typ in ["ip", "file", "certificate"]: - if typ != "ip": - typ_plurial = typ + "s" - else: - typ_plurial = typ - if typ in service: - new_service[typ_plurial] = {} - for elt in service[typ]: - name, new_elt = getattr(self, f"parse_{typ}")(elt) - new_service[typ_plurial][name] = new_elt - if "override" in service: - if isinstance(service["override"], list): - new_service["override"] = service["override"][0] - else: - new_service["override"] = service["override"] - if "servicelist" in service: - self.lists["service"].setdefault( - service["servicelist"], [] - ).append(new_service) - name = ( - service.pop("name") + "." + service.get("type", "service") - ) - self.services[name] = new_service - - def parse_ip(self, ip: dict) -> None: - name = self.get_variable_path(ip.pop("text")) - if "iplist" in ip: - self.lists["ip"].setdefault(ip.pop("iplist"), []).append(ip) - if "netmask" in ip: - ip["netmask"] = self.get_variable_path(ip["netmask"]) - return name, ip - - def parse_file(self, file: dict) -> None: - name = file.pop("text") - if "file_type" in file: - file["type"] = file.pop("file_type") - if file["type"] == "variable": - name = self.get_variable_path(name) - if "variable_type" in file: - file.pop("variable_type") - if "filelist" in file: - self.lists["file"].setdefault(file.pop("filelist"), []).append(file) - for typ in ["source", "owner", "group"]: - if f"{typ}_type" in file: - obj_type = file.pop(f"{typ}_type") - if obj_type == "variable" and typ in file: - file[typ] = { - "name": self.get_variable_path(file[typ]), - "type": "variable", - } - if "variable" in file: - file["variable"] = self.get_variable_path(file["variable"]) - return name, file - - def parse_certificate(self, certificate: dict) -> None: - name = certificate.pop("text") - if "variable" in certificate: - certificate["rougail_variable"] = certificate["variable"] - if "certificate_type" in certificate: - if certificate.pop("certificate_type") == "variable": - certificate["variable"] = True - name = self.get_variable_path(name) - if "certificatelist" in certificate: - self.lists["certificate"].setdefault( - certificate.pop("certificatelist"), [] - ).append(certificate) - for typ in ["owner", "group", "server", "domain", "provider"]: - if f"{typ}_type" in certificate: - obj_type = certificate.pop(f"{typ}_type") - if obj_type == "variable" and typ in certificate: - certificate[typ] = { - "name": self.get_variable_path(certificate[typ]), - "type": "variable", - } - return name, certificate - - def parse_constraints( - self, - dico: dict, - ) -> None: - if "constraints" not in dico: - return - for constraint in dico["constraints"]: - if "condition" in constraint: - for condition in constraint["condition"]: - self.parse_condition(condition) - if "check" in constraint: - for check in constraint["check"]: - self.parse_check(check) - if "fill" in constraint: - for fill in constraint["fill"]: - self.parse_fill(fill) - - def parse_condition( - self, - condition: dict, - ) -> None: - if "apply_on_fallback" in condition: - apply_on_fallback = condition.pop("apply_on_fallback") - else: - apply_on_fallback = False - source = self.get_variable_path(condition["source"]) - if not 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"), multi - ) - if force_param: - condition_value.setdefault("params", {}).update(force_param) - 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] - variable[prop] = condition_value - elif typ == "family": - family_path = self.get_family_path(target["text"]) - if family_path is None: - continue - family = self.paths["family"][family_path] - family[prop] = condition_value - elif typ == "iplist": - list_name = target["text"] - if list_name in self.lists["ip"]: - for ip in self.lists["ip"].pop(list_name): - ip[prop] = condition_value - elif typ == "filelist": - list_name = target["text"] - if list_name in self.lists["file"]: - for ip in self.lists["file"].pop(list_name): - ip[prop] = condition_value - elif typ == "servicelist": - list_name = target["text"] - if list_name in self.lists["service"]: - for service in self.lists["service"].pop(list_name): - service[prop] = condition_value - elif typ == "certificatelist": - list_name = target["text"] - if list_name in self.lists["certificate"]: - for certificat in self.lists["certificate"].pop(list_name): - certificat[prop] = condition_value - - def parse_check( - self, - check: dict, - ) -> None: - for target in check["target"]: - variable_path = self.get_variable_path(target["text"]) - if variable_path is None: - continue - variable = self.paths["variable"][variable_path] - if "validators" in variable and variable["validators"] is None: - variable.pop("validators") - if check.get("type") == "jinja": - check_value = check["name"] - else: - check["param"] = [ - {"text": variable_path, "type": "variable"} - ] + check.get("param", []) - check_value = self.convert_param_function( - check, variable.get("multi", False) - ) - variable.setdefault("validators", []).append(check_value) - - def parse_fill( - self, - fill: dict, - ) -> None: - for target in fill.pop("target"): - params = [] - variable_path = self.get_variable_path(target["text"]) - 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 = { - "type": "jinja", - "jinja": fill["name"], - } - else: - 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, - prop: str, - path: str, - params: List[dict], - if_in: bool, - multi: bool, - ) -> str: - new_params = {} - jinja = "{% if " - for idx, param in enumerate(params): - if idx: - jinja += " or " - - new_param, value = self.get_jinja_param_and_value(param, multi) - if value: - jinja += path + " == " + value - if new_param: - new_params |= new_param - if if_in: - jinja += " %}" + prop + "{% endif %}" - else: - jinja += " %}{% else %}" + prop + "{% endif %}" - ret = { - "type": "jinja", - "jinja": jinja, - } - if new_params: - ret["params"] = new_params - return ret - - def get_value( - self, - param: dict, - multi: bool, - ) -> Any: - typ = param.get("type", "string") - if typ == "string": - value = param.get("text") - elif typ == "number": - value = int(param["text"]) - elif typ == "nil": - value = None - elif typ == "space": - value = " " - elif typ == "boolean": - value = param["text"] - elif typ == "variable": - variable_path = self.get_variable_path(param["text"]) - if variable_path is None: - variable_path = "__" + param["text"] - value = { - "type": "variable", - "variable": variable_path, - } - if "optional" in param: - value["optional"] = param["optional"] - if "propertyerror" in param: - value["propertyerror"] = param["propertyerror"] - elif typ == "function": - value = self.convert_param_function(param, multi) - elif typ == "information": - value = { - "type": "information", - "information": param["text"], - } - if "variable" in param: - variable_path = self.get_variable_path(param["variable"]) - value["variable"] = variable_path - elif typ == "suffix": - value = param - elif typ == "index": - value = param - return value - - 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, multi) - if isinstance(value, dict): - if typ == "information": - key = normalize_family(value["information"]) - if "variable" in value: - attr_name = f'{value["variable"]}.{key}' - else: - attr_name = key - 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: {"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: - return - else: - value = dumps(value, ensure_ascii=False) - return new_param, value - - 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, 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, 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) - text += "(" - text += ", ".join(values) - 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 - return ret - - def get_variable_path( - self, - path: str, - ) -> dict: - if ( - path not in self.paths["variable"] - 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 path - return path - - def get_family_path( - self, - path: str, - ) -> dict: - if path not in self.paths["family"] and path in self.flatten_paths["family"]: - path = self.flatten_paths["family"][path] - if path not in self.paths["family"]: - return - return path - - def get(self) -> dict: - return self.variables, self.services - - -class RougailUpgrade: - def __init__( - self, - test=False, - upgrade_help=None, - rougailconfig: RougailConfig = None, - ) -> None: - self.test = test - if upgrade_help is None: - upgrade_help = {} - self.upgrade_help = upgrade_help - if rougailconfig is None: - rougailconfig = RougailConfig - self.rougailconfig = rougailconfig - - def run( - self, - ): - for dict_dir, dest_dir in zip( - self.rougailconfig["main_dictionaries"], - self.rougailconfig["upgrade_options.main_dictionaries"], - ): - self._load_dictionaries( - dict_dir, - 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 = Path(dst_extra_dir) / namespace - if not extra_dstsubfolder.is_dir(): - extra_dstsubfolder.mkdir() - for extra_dir in extra_dirs: - self._load_dictionaries( - str(extra_dir), - str(extra_dstsubfolder), - normalize_family(namespace), - ) - - def _load_dictionaries( - self, - srcfolder: 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) - if filename.endswith(".xml") or filename.endswith(".yml") - ] - filenames.sort() - for filename in filenames: - xmlsrc = Path(srcfolder) / Path(filename) - - ymldst = Path(dstfolder) / (Path(filename).stem + ".yml") - if filename.endswith(".xml"): - if parse is None: - raise Exception("XML module is not installed") - try: - parser = XMLParser(remove_blank_text=True) - document = parse(xmlsrc, parser) - except XMLSyntaxError as err: - raise Exception(_("not a XML file: {0}").format(err)) from err - root = document.getroot() - search_function_name = get_function_name( - root.attrib.get("version", "1") - ) - ext = "xml" - else: - with xmlsrc.open() as file_fh: - root = YAML(typ="safe").load(file_fh) - search_function_name = get_function_name(str(root["version"])) - ext = "yml" - function_found = False - root_services = None - for version, function_version in FUNCTION_VERSIONS: - if function_found and hasattr(self, function_version): - upgrade_help = self.upgrade_help.get(function_version, {}).get( - filename, {} - ) - if upgrade_help.get("remove") is True: - continue - root, root_services_, new_type = getattr(self, function_version)( - root, upgrade_help, namespace, xmlsrc, ext - ) - if root_services_ is not None: - root_services = root_services_ - if function_version == search_function_name: - function_found = True - if root != {"version": None}: - root["version"] = float(version) - with ymldst.open("w") as ymlfh: - yaml = YAML() - yaml.dump( - root, - 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 [ - "mandatory", - "hidden", - "redefine", - "multi", - "leadership", - "optional", - "unique", - "auto_save", - "remove_check", - "manage", - "exists", - "disabled", - "undisable", - "remove_choice", - "propertyerror", - "apply_on_fallback", - "remove_fill", - "remove_condition", - ]: - if prop in variable: - variable[prop] = {"True": True, "False": False}[variable[prop]] - - def _attribut_to_int(self, variable): - for prop in ["mode"]: - if prop in variable: - if variable[prop] in ["expert", "normal"]: - variable[prop] = { - "expert": "advanced", - "normal": "standard", - }.get(variable[prop]) - continue - try: - variable[prop] = int(variable[prop]) - except ValueError: - pass - - def _xml_to_yaml( - self, - objects, - obj_name, - variables, - path, - variable_type="string", - variable_choices=[], - ): - if obj_name in ["variables", "family"]: - dico = [] - else: - dico = {} - for obj in objects: - obj_type = obj.tag - if not isinstance(obj_type, str): - # doesn't proceed the XML commentaries - continue - new_dico = dict(obj.attrib) - if obj_type in ["variable", "family"]: - if path: - path += "." + obj.attrib["name"] - else: - path = obj.attrib["name"] - choices = [] - if obj_type == "variable": - var_type = obj.attrib.get("type", "string") - variables[obj.attrib["name"]] = {"type": var_type} - variables[path] = {"type": var_type} - elif obj_type == "condition": - var_type = variables.get(obj.attrib["source"], {}).get("type", "string") - if var_type == "choice": - choices = variables.get(obj.attrib["source"], {}).get("choices", []) - else: - var_type = None - new_objects = self._xml_to_yaml( - obj, obj.tag, variables, path, var_type, choices - ) - if obj.text: - text = obj.text - if isinstance(text, str): - text = text.strip() - if text: - if obj_type in ["choice", "value"]: - value_type = obj.attrib.get("type") - if not value_type and obj_type == "value": - value_type = variable_type - text = CONVERT_OPTION.get(value_type, {}).get("func", str)(text) - if obj_type == "choice": - variables[path.rsplit(".", 1)[-1]].setdefault( - "choices", [] - ).append({"type": value_type, "value": text}) - variables[path].setdefault("choices", []).append( - {"type": value_type, "value": text} - ) - if obj_type in ["param", "value"]: - if obj.attrib.get("type") == "variable": - var_type = variables.get(obj.attrib.get("name"), {}).get( - "type", "string" - ) - value_type = obj.attrib.get("type", var_type) - text = CONVERT_OPTION.get(value_type, {}).get("func", str)( - text - ) - elif "type" in obj.attrib: - var_type = obj.attrib.get("type") - text = CONVERT_OPTION.get(var_type, {}).get("func", str)( - text - ) - elif obj_type == "param" and variable_type: - if variable_type == "choice": - for choice in variable_choices: - if choice["value"] == CONVERT_OPTION.get( - choice.get("type"), {} - ).get("func", str)(text): - text = choice["value"] - break - else: - text = CONVERT_OPTION.get(variable_type, {}).get( - "func", str - )(text) - new_dico["text"] = text - if isinstance(new_objects, list): - if new_objects: - for new_obj in new_objects: - new_dico.update(new_obj) - elif new_objects is not None and list(new_objects.values())[0]: - new_dico.update(new_objects) - self._attribut_to_bool(new_dico) - self._attribut_to_int(new_dico) - if not new_dico: - new_dico = None - if obj_type == "override" and not new_dico: - new_dico = None - if isinstance(dico, list): - if dico and obj_type in dico[-1]: - dico[-1][obj_type].append(new_dico) - else: - dico.append({obj_type: [new_dico]}) - elif new_dico is None: - dico[obj_type] = new_dico - else: - dico.setdefault(obj_type, []).append(new_dico) - if dico == {}: - dico = None - elif isinstance(dico, dict): - dico = [dico] - if obj_name in ["service", "condition", "fill", "choice", "check"]: - pass - elif obj_name in ["variables", "family"]: - dico = {"variables": dico} - elif obj_name != "variable": - dico = {obj_name: dico} - return dico - - def _update_1_1(self, root): - 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 value and isinstance(value["variable"], str)) - or ("_variable" in value and isinstance(value["_variable"], str)) - ) and ( - ("_type" in value and value["_type"] == "dynamic") - or ("type" in value and value["type"] == "dynamic") - ): - value["dynamic"] = { - "type": "variable", - "variable": value.pop("variable"), - "propertyerror": False, - } - 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, - root, - upgrade_help: dict, - namespace: str, - xmlsrc: str, - ext: str, - ): - self._update_1_1(root) - return root, None, "yml" - - def update_1_0( - self, - root: "Element", - upgrade_help: dict, - namespace: str, - xmlsrc: str, - ext: str, - ) -> "Element": - if ext == "xml": - new_root = {"version": root.attrib["version"]} - variables = {} - for typ in ["services", "variables", "constraints"]: - objects = root.find(typ) - if objects is None: - objects = [] - new_objects = self._xml_to_yaml(objects, typ, variables, namespace) - if new_objects[typ]: - new_root.update(new_objects) - else: - new_root = root - variables, services = upgrade_010_to_10(new_root, namespace, xmlsrc).get() - return variables, services, "yml" - - def update_0_10( - self, - root: "Element", - upgrade_help: dict, - namespace: str, - xmlsrc: str, - ext: str, - ) -> "Element": - variables = root.find("variables") - if variables is None: - return root - paths = self._get_path_variables( - variables, - namespace == "configuration", - namespace, - ) - constraints = root.find("constraints") - # convert schedule and schedulemod - for variable in paths.values(): - variable = variable["variable"] - if variable.tag != "variable": - continue - if "type" in variable.attrib and variable.attrib["type"] in [ - "schedule", - "schedulemod", - ]: - if variable.attrib["type"] == "schedule": - choices = ("none", "daily", "weekly", "monthly") - else: - choices = ("pre", "post") - variable.attrib["type"] = "choice" - has_value = False - for value in variable: - if value.tag == "value": - has_value = True - break - for name in choices: - choice = SubElement(variable, "choice") - choice.text = name - if not has_value: - value = SubElement(variable, "value") - value.text = choices[0] - - # convert group to leadership - groups = [] - if constraints is not None: - for constraint in constraints: - if constraint.tag == "group": - constraints.remove(constraint) - groups.append(constraint) - for group in groups: - if group.attrib["leader"] in paths: - leader_obj = paths[group.attrib["leader"]] - # FIXME name peut avoir "." il faut le virer - # FIXME si extra c'est un follower ! - if "name" in group.attrib: - grpname = group.attrib["name"] - if "description" in group.attrib: - description = group.attrib["description"] - else: - description = grpname - else: - grpname = leader_obj["variable"].attrib["name"] - if "." in grpname: - grpname = grpname.rsplit(".", 1)[-1] - if "description" in group.attrib: - description = group.attrib["description"] - elif "description" in leader_obj["variable"].attrib: - description = leader_obj["variable"].attrib["description"] - else: - description = grpname - family = SubElement( - leader_obj["parent"], - "family", - name=grpname, - description=description, - leadership="True", - ) - leader_obj["parent"].remove(leader_obj["variable"]) - family.append(leader_obj["variable"]) - else: - # append in group - follower = next(iter(group)) - leader_name = group.attrib["leader"] - if "." in leader_name: - leader_path = leader_name.rsplit(".", 1)[0] - follower_path = leader_path + "." + follower.text - else: - follower_path = follower.text - obj = paths[follower_path] - family = SubElement( - obj["parent"], "family", name=leader_name, leadership="True" - ) - grpname = leader_name - for follower in group: - leader_name = group.attrib["leader"] - if "." in leader_name: - leader_path = leader_name.rsplit(".", 1)[0] - follower_path = leader_path + "." + follower.text - else: - follower_path = follower.text - follower_obj = paths[follower_path] - follower_obj["parent"].remove(follower_obj["variable"]) - family.append(follower_obj["variable"]) - if "." in follower_path: - new_path = ( - follower_path.rsplit(".", 1)[0] - + "." - + grpname - + "." - + follower_path.rsplit(".", 1)[1] - ) - paths[new_path] = paths[follower_path] - - # convert choice option - valid_enums = [] - if constraints is not None: - for constraint in constraints: - if ( - constraint.tag == "check" - and constraint.attrib["name"] == "valid_enum" - ): - constraints.remove(constraint) - valid_enums.append(constraint) - for valid_enum in valid_enums: - targets = [] - for target in valid_enum: - if target.tag != "target": - continue - if target.text in paths: - # not in paths if it's optional - # but not check it - targets.append(paths[target.text]["variable"]) - params = [] - function_param = None - for param in valid_enum: - if param.tag != "param": - continue - if "type" in param.attrib and param.attrib["type"] == "function": - function_param = param.text - continue - params.append(param) - first_choice = None - for target in targets: - if function_param is not None: - function = SubElement( - target, "choice", type="function", name=function_param - ) - for param in params: - if function_param is not None: - function.append(param) - else: - choice = SubElement(target, "choice") - if first_choice is None and ( - "type" not in param.attrib - or param.attrib["type"] != "variable" - ): - first_choice = choice - choice.text = param.text - if "type" not in param.attrib and param.text is None: - choice_type = "nil" - elif "type" in param.attrib: - choice_type = param.attrib["type"] - elif "type" in target.attrib: - choice_type = target.attrib["type"] - else: - choice_type = "string" - choice.attrib["type"] = choice_type - has_value = False - for target in targets: - if "remove_check" in target.attrib: - target.attrib["remove_choice"] = target.attrib["remove_check"] - for target in targets: - for value in target: - if value.tag == "value": - has_value = True - if "type" in target.attrib: - value.attrib["type"] = target.attrib["type"] - if first_choice is not None and not has_value: - value = SubElement(target, "value") - value.attrib["type"] = first_choice.attrib["type"] - value.text = first_choice.text - for target in targets: - if ( - "remove_choice" not in target.attrib - or target.attrib["remove_choice"] != "True" - ): - target.attrib["type"] = "choice" - return root, None, "xml" - - def _get_path_variables(self, variables, is_variable_namespace, path, dico=None): - if dico is None: - dico = {} - for variable in variables: - if not is_variable_namespace and path: - subpath = path + "." - else: - subpath = "" - if variable.tag not in ["variable", "family"]: - continue - subpath += variable.attrib["name"] - if variable.tag == "family": - self._get_path_variables(variable, is_variable_namespace, subpath, dico) - elif variable.tag == "variable": - dico[subpath] = {"variable": variable, "parent": variables} - return dico - - @staticmethod - def move(elt, src, dst, optional=False): - if src == "text": - value = elt.text - elt.text = None - else: - if optional and src not in elt.attrib: - return - value = elt.attrib[src] - del elt.attrib[src] - # - if dst == "text": - elt.text = value - else: - elt.attrib[dst] = value - - @staticmethod - def remove(elt, src, optional=False): - if optional and src not in elt.attrib: - return - del elt.attrib[src] - - @staticmethod - def create_service(services, service_name, service_elt, servicelists, upgrade_help): - if service_name in service_elt: - return service_elt[service_name] - service = SubElement(services, "service") - service.attrib["name"] = service_name - if service_name == "unknown": - service.attrib["manage"] = "False" - if service_name in upgrade_help.get("services", {}).get("unmanage", []): - service.attrib["manage"] = "False" - service_elt[service_name] = service - if upgrade_help.get("servicelists", {}).get(service_name): - service.attrib["servicelist"] = upgrade_help.get("servicelists", {}).get( - service_name - ) - elif service_name in servicelists: - service.attrib["servicelist"] = servicelists[service_name] - return service - - def upgrade_container( - self, elt, current_service, files, ip, servicelists, upgrade_help - ): - if elt.tag == "file": - self.move(elt, "name", "text") - self.remove(elt, "del_comment", optional=True) - elt.attrib["engine"] = "creole_legacy" - if ( - not "instance_mode" in elt.attrib - or elt.attrib["instance_mode"] != "when_container" - ) and elt.text not in upgrade_help.get("files", {}).get("remove", {}): - if elt.attrib.get("filelist") in upgrade_help.get("services", {}).get( - "filelist_service", {} - ): - elt_service = upgrade_help.get("services", {}).get( - "filelist_service", {} - )[elt.attrib["filelist"]] - if elt_service in files: - service = elt_service - else: - service = current_service - else: - service = current_service - files[service][elt.text] = elt - elif elt.tag in [ - "host", - "disknod", - "fstab", - "interface", - "package", - "service_access", - ]: - pass - elif elt.tag == "service_restriction": - for restriction in elt: - if restriction.tag == "ip" and restriction.text != "0.0.0.0": - self.remove(restriction, "ip_type", optional=True) - self.remove(restriction, "netmask_type", optional=True) - if elt.attrib["service"] in upgrade_help.get("services", {}).get( - "rename", {} - ): - elt_service = upgrade_help.get("services", {}).get( - "rename", {} - )[elt.attrib["service"]] - else: - elt_service = elt.attrib["service"] - if elt_service in ip: - service = elt_service - else: - service = current_service - ip[service].append(restriction) - elif elt.tag == "service": - new_name = elt.text - if current_service == "unknown": - if new_name in files: - raise Exception("hu?") - files[new_name] = files[current_service] - del files[current_service] - ip[new_name] = ip[current_service] - del ip[current_service] - elif new_name not in files: - files[new_name] = {} - ip[new_name] = [] - current_service = new_name - if "servicelist" in elt.attrib: - servicelists[current_service] = elt.attrib["servicelist"] - else: - raise Exception(f"unknown containers tag {elt.tag}") - return current_service diff --git a/src/rougail/user_datas.py b/src/rougail/user_datas.py index fb0424de9..a72916f67 100644 --- a/src/rougail/user_datas.py +++ b/src/rougail/user_datas.py @@ -1,6 +1,6 @@ """ Silique (https://www.silique.fr) -Copyright (C) 2024 +Copyright (C) 2022-2024 distribued with GPL-2 or later license @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ + from typing import List from re import findall @@ -76,8 +77,7 @@ class UserDatas: self._not_found_is_dynamic(self.config, path, cache, added) def _not_found_is_dynamic(self, config, path, cache, added): - """if path is not found, check if parent is a dynamic family - """ + """if path is not found, check if parent is a dynamic family""" current_path = "" identifiers = [] # get parent @@ -102,9 +102,7 @@ class UserDatas: if not tconfig.isdynamic(only_self=True): # it's not a dynamic variable continue - identifier = self._get_identifier( - tconfig.name(), name - ) + identifier = self._get_identifier(tconfig.name(), name) if identifier is None: # it's a dynamic variable but doesn't match the current name continue @@ -116,11 +114,13 @@ class UserDatas: # it's the good dynamic variable but it's not linked to a variable # so cannot change the variable continue - option_type = self.config.option( - dynamic_variable - ).information.get("type") - dyn_options_values = self.config.option(dynamic_variable).get().impl_getdefault() - if "{{ identifier }}" in dynamic_variable: + option_type = self.config.option(dynamic_variable).information.get( + "type" + ) + dyn_options_values = ( + self.config.option(dynamic_variable).get().impl_getdefault() + ) + if "{{ identifier }}" in dynamic_variable: for s in identifiers: dynamic_variable = dynamic_variable.replace( "{{ identifier }}", str(s), 1 @@ -132,18 +132,11 @@ class UserDatas: continue config = tconfig identifiers.append(identifier) - typ = CONVERT_OPTION.get(option_type, {}).get( - "func" - ) + typ = CONVERT_OPTION.get(option_type, {}).get("func") if typ: identifier = typ(identifier) - if ( - identifier - not in self.values[dynamic_variable]["values"] - ): - self.values[dynamic_variable]["values"].append( - identifier - ) + if identifier not in self.values[dynamic_variable]["values"]: + self.values[dynamic_variable]["values"].append(identifier) cache[current_path] = config, identifier break @@ -211,6 +204,11 @@ class UserDatas: for path, data in self.values.items(): try: option = self.config.option(path) + if option.isoptiondescription(): + self.errors.append( + f'the option "{option.path()}" is an option description' + ) + continue value = data["values"] if option.isfollower(): for index, val in enumerate(value): @@ -231,7 +229,7 @@ def convert_value(option, value): if value == "": return None option_type = option.information.get("type") - if option_type == 'choice': + if option_type == "choice": choices = option.value.list() if value not in choices and isinstance(value, str): # FIXME add other tests (boolean, float, ...) diff --git a/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/base.py b/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/base.py index ca23a5356..f6238974d 100644 --- a/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/base.py +++ b/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/base.py @@ -11,6 +11,6 @@ ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_2 = StrOption(name="my_variable", doc="my_variable", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_3 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_3 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_2)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) diff --git a/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/no_namespace.py b/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/no_namespace.py index 01a08ec5d..61e3fbf55 100644 --- a/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/no_namespace.py +++ b/tests/dictionaries/00_9default_calculation_multi_optional/tiramisu/no_namespace.py @@ -7,5 +7,5 @@ ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_1 = StrOption(name="my_variable", doc="my_variable", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_2 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_2 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2]) diff --git a/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/base.py b/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/base.py index de0e24cae..9045a4883 100644 --- a/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/base.py +++ b/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/base.py @@ -11,6 +11,6 @@ ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_2 = StrOption(name="my_variable", doc="my_variable", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_3 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)}))], properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_3 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2))))], properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3], properties=frozenset({"standard"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) diff --git a/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/no_namespace.py b/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/no_namespace.py index 850c695ee..0bd4afbe0 100644 --- a/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/no_namespace.py +++ b/tests/dictionaries/00_9default_calculation_multi_optional2/tiramisu/no_namespace.py @@ -7,5 +7,5 @@ ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_1 = StrOption(name="my_variable", doc="my_variable", default="val1", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_2 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)}))], properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_2 = StrOption(name="my_calculated_variable", doc="my_calculated_variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1))))], properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2]) diff --git a/tests/dictionaries/20_9family_absolute/makedict/after.json b/tests/dictionaries/20_9family_absolute/makedict/after.json index 246e5a874..7fe18898e 100644 --- a/tests/dictionaries/20_9family_absolute/makedict/after.json +++ b/tests/dictionaries/20_9family_absolute/makedict/after.json @@ -18,12 +18,16 @@ "owner": "default", "value": null }, + "rougail.family2.var3": { + "owner": "default", + "value": "string4" + }, "rougail.family2.subfamily.variable": { "owner": "default", "value": [ null, null, - null + "string4" ] } } diff --git a/tests/dictionaries/20_9family_absolute/makedict/base.json b/tests/dictionaries/20_9family_absolute/makedict/base.json index a9cbbfae2..55c961cdf 100644 --- a/tests/dictionaries/20_9family_absolute/makedict/base.json +++ b/tests/dictionaries/20_9family_absolute/makedict/base.json @@ -6,9 +6,10 @@ null ], "rougail.family2.var2": null, + "rougail.family2.var3": "string4", "rougail.family2.subfamily.variable": [ null, null, - null + "string4" ] } diff --git a/tests/dictionaries/20_9family_absolute/makedict/before.json b/tests/dictionaries/20_9family_absolute/makedict/before.json index 246e5a874..7fe18898e 100644 --- a/tests/dictionaries/20_9family_absolute/makedict/before.json +++ b/tests/dictionaries/20_9family_absolute/makedict/before.json @@ -18,12 +18,16 @@ "owner": "default", "value": null }, + "rougail.family2.var3": { + "owner": "default", + "value": "string4" + }, "rougail.family2.subfamily.variable": { "owner": "default", "value": [ null, null, - null + "string4" ] } } diff --git a/tests/dictionaries/20_9family_absolute/tiramisu/no_namespace.py b/tests/dictionaries/20_9family_absolute/tiramisu/no_namespace.py index 88afed109..d8bb30e9a 100644 --- a/tests/dictionaries/20_9family_absolute/tiramisu/no_namespace.py +++ b/tests/dictionaries/20_9family_absolute/tiramisu/no_namespace.py @@ -7,12 +7,13 @@ ALLOWED_LEADER_PROPERTIES.add("basic") ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_1 = StrOption(name="var1", doc="first variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) -option_3 = StrOption(name="var2", doc="a second variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) -option_5 = StrOption(name="variable", doc="third variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(False)})), Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(False)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(False)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_3 = StrOption(name="var2", doc="a second variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string', 'test': ('string6',)}) +option_5 = StrOption(name="variable", doc="third variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)))), Calculation(func['calc_value'], Params((ParamOption(option_3))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) optiondescription_4 = OptionDescription(name="subfamily", doc="a sub family", children=[option_5], properties=frozenset({"standard"})) optiondescription_2 = OptionDescription(name="family", doc="a family", children=[option_3, optiondescription_4], properties=frozenset({"basic"})) -option_7 = StrOption(name="var2", doc="var2", default=Calculation(func['calc_value'], Params((ParamOption(option_3)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_9 = StrOption(name="variable", doc="fourth variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(False)})), Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(False)})), Calculation(func['calc_value'], Params((ParamOption(option_7)), kwargs={'__internal_multi': ParamValue(False)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(False)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -optiondescription_8 = OptionDescription(name="subfamily", doc="a sub family", children=[option_9], properties=frozenset({"standard"})) -optiondescription_6 = OptionDescription(name="family2", doc="a family", children=[option_7, optiondescription_8], properties=frozenset({"standard"})) +option_7 = StrOption(name="var2", doc="a variable2", default=Calculation(func['calc_value'], Params((ParamOption(option_3)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_8 = StrOption(name="var3", doc="var3", default="string4", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string', 'test': ('string5',)}) +option_10 = StrOption(name="variable", doc="fourth variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)))), Calculation(func['calc_value'], Params((ParamOption(option_3)))), Calculation(func['calc_value'], Params((ParamOption(option_8))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +optiondescription_9 = OptionDescription(name="subfamily", doc="a sub family", children=[option_10], properties=frozenset({"standard"})) +optiondescription_6 = OptionDescription(name="family2", doc="a family", children=[option_7, option_8, optiondescription_9], properties=frozenset({"standard"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, optiondescription_2, optiondescription_6]) diff --git a/tests/dictionaries/20_9family_absolute/tmp/no_namespace.py b/tests/dictionaries/20_9family_absolute/tmp/no_namespace.py deleted file mode 100644 index 9068b3323..000000000 --- a/tests/dictionaries/20_9family_absolute/tmp/no_namespace.py +++ /dev/null @@ -1,18 +0,0 @@ -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('../rougail-tests/funcs/test.py') -ALLOWED_LEADER_PROPERTIES.add("basic") -ALLOWED_LEADER_PROPERTIES.add("standard") -ALLOWED_LEADER_PROPERTIES.add("advanced") -option_1 = StrOption(name="var1", doc="first variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) -option_3 = StrOption(name="var2", doc="a second variable", properties=frozenset({"basic", "mandatory"}), informations={'type': 'string'}) -option_5 = StrOption(name="variable", doc="third variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -optiondescription_4 = OptionDescription(name="subfamily", doc="a sub family", children=[option_5], properties=frozenset({"standard"})) -optiondescription_2 = OptionDescription(name="family", doc="a family", children=[option_3, optiondescription_4], properties=frozenset({"basic"})) -option_7 = StrOption(name="var2", doc="var2", default=Calculation(func['calc_value'], Params((ParamOption(option_3)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_9 = StrOption(name="variable", doc="fourth variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(True)})), Calculation(func['calc_value'], Params((ParamOption(option_7)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_1)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -optiondescription_8 = OptionDescription(name="subfamily", doc="a sub family", children=[option_9], properties=frozenset({"standard"})) -optiondescription_6 = OptionDescription(name="family2", doc="a family", children=[option_7, optiondescription_8], properties=frozenset({"standard"})) -option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, optiondescription_2, optiondescription_6]) diff --git a/tests/dictionaries/40_8calculation_multi_variable/tiramisu/base.py b/tests/dictionaries/40_8calculation_multi_variable/tiramisu/base.py index 8e25e8df0..87c457275 100644 --- a/tests/dictionaries/40_8calculation_multi_variable/tiramisu/base.py +++ b/tests/dictionaries/40_8calculation_multi_variable/tiramisu/base.py @@ -12,6 +12,6 @@ ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_3 = StrOption(name="var2", doc="a second variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) option_4 = StrOption(name="var3", doc="a third variable", default="yes", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_2 = StrOption(name="var", doc="a first variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(True)})), Calculation(func['calc_value'], Params((ParamOption(option_4)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_2 = StrOption(name="var", doc="a first variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_3)))), Calculation(func['calc_value'], Params((ParamOption(option_4))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_3)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) optiondescription_1 = OptionDescription(name="rougail", doc="Rougail", group_type=groups.namespace, children=[option_2, option_3, option_4], properties=frozenset({"standard"})) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1]) diff --git a/tests/dictionaries/40_8calculation_multi_variable/tiramisu/no_namespace.py b/tests/dictionaries/40_8calculation_multi_variable/tiramisu/no_namespace.py index 78de98035..e6a3bb1db 100644 --- a/tests/dictionaries/40_8calculation_multi_variable/tiramisu/no_namespace.py +++ b/tests/dictionaries/40_8calculation_multi_variable/tiramisu/no_namespace.py @@ -8,5 +8,5 @@ ALLOWED_LEADER_PROPERTIES.add("standard") ALLOWED_LEADER_PROPERTIES.add("advanced") option_2 = StrOption(name="var2", doc="a second variable", default="no", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) option_3 = StrOption(name="var3", doc="a third variable", default="yes", properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) -option_1 = StrOption(name="var", doc="a first variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)})), Calculation(func['calc_value'], Params((ParamOption(option_3)), kwargs={'__internal_multi': ParamValue(True)}))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_2)), kwargs={'__internal_multi': ParamValue(True)})), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) +option_1 = StrOption(name="var", doc="a first variable", multi=True, default=[Calculation(func['calc_value'], Params((ParamOption(option_2)))), Calculation(func['calc_value'], Params((ParamOption(option_3))))], default_multi=Calculation(func['calc_value'], Params((ParamOption(option_2)))), properties=frozenset({"mandatory", "standard"}), informations={'type': 'string'}) option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_2, option_3]) diff --git a/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/after.json b/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/after.json index 51d3a58e2..a20cae471 100644 --- a/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/after.json +++ b/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/after.json @@ -12,7 +12,7 @@ "default" ], "value": [ - "ne peut acc\u00e9der \u00e0 l'option \"a follower\" \u00e0 cause de la propri\u00e9t\u00e9 \"disabled\" (the value of \"leader\" is \"a\")", + "cannot access to option \"a follower\" because has property \"disabled\" (the value of \"leader\" is \"a\")", "b" ] } diff --git a/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/before.json b/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/before.json index 51d3a58e2..a20cae471 100644 --- a/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/before.json +++ b/tests/dictionaries/44_9calculated_default_leadership_leader/makedict/before.json @@ -12,7 +12,7 @@ "default" ], "value": [ - "ne peut acc\u00e9der \u00e0 l'option \"a follower\" \u00e0 cause de la propri\u00e9t\u00e9 \"disabled\" (the value of \"leader\" is \"a\")", + "cannot access to option \"a follower\" because has property \"disabled\" (the value of \"leader\" is \"a\")", "b" ] } diff --git a/tests/test_1_flattener.py b/tests/test_1_flattener.py index f348c63d4..aec9458e8 100644 --- a/tests/test_1_flattener.py +++ b/tests/test_1_flattener.py @@ -45,7 +45,7 @@ excludes = set([ ]) test_ok -= excludes test_raise -= excludes -#test_ok = ['00_9extra'] +# test_ok = ['00_9default_calculation_multi_optional'] #test_ok = [] #test_raise = ['88valid_enum_invalid_default'] #test_raise = [] @@ -124,7 +124,7 @@ def save(test_dir, eolobj, multi=False, namespace=False, error=False): tiramisu_dir = dirname(tiramisu_file) if not error: if not isdir(tiramisu_dir): - raise Exception(f'creates {tiramisu_dir}') + raise Exception(f'please creates {tiramisu_dir}') if not isfile(tiramisu_file) or debug: copyfile(tiramisu_tmp, tiramisu_file) with open(tiramisu_tmp, 'r') as fh: @@ -161,28 +161,32 @@ def test_dictionary_namespace(test_dir): test_dir_ = join(dico_dirs, test_dir) rougailconfig = RougailConfig.copy() rougailconfig['main_namespace'] = 'Rougail' + if (dico_dirs / test_dir / 'force_no_namespace').is_file(): + return eolobj = load_rougail_object(test_dir_, rougailconfig, namespace=True) if not eolobj: return save(test_dir_, eolobj, namespace=True) assert getcwd() == ORI_DIR - - -def test_dictionary_multi(test_dir): - if not test_multi: - print('MULTI!') - return - assert getcwd() == ORI_DIR - test_dir_ = join(dico_dirs, test_dir) - rougailconfig = RougailConfig.copy() - rougailconfig['main_namespace'] = 'Rougail' - eolobj = load_rougail_object(test_dir_, rougailconfig, multi=True) - if not eolobj: - return - eolobj.add_path_prefix('1') - eolobj.add_path_prefix('2') - save(test_dir_, eolobj, multi=True) - assert getcwd() == ORI_DIR +# +# +#def test_dictionary_multi(test_dir): +# if not test_multi: +# print('MULTI!') +# return +# assert getcwd() == ORI_DIR +# test_dir_ = join(dico_dirs, test_dir) +# rougailconfig = RougailConfig.copy() +# rougailconfig['main_namespace'] = 'Rougail' +# if (dico_dirs / test_dir / 'force_no_namespace').is_file(): +# return +# eolobj = load_rougail_object(test_dir_, rougailconfig, multi=True) +# if not eolobj: +# return +# eolobj.add_path_prefix('1') +# eolobj.add_path_prefix('2') +# save(test_dir_, eolobj, multi=True) +# assert getcwd() == ORI_DIR def test_error_dictionary(test_dir_error): diff --git a/tests/test_2_makedict.py b/tests/test_2_makedict.py index e41e36c83..5c110cab3 100644 --- a/tests/test_2_makedict.py +++ b/tests/test_2_makedict.py @@ -85,6 +85,8 @@ def launch_flattener(test_dir, mandatory_file = Path(makedict_dir) / 'mandatory.json' modulepath = join(test_dir, 'tiramisu', filename + '.py') + if not isfile(modulepath): + return with open(modulepath) as fh: optiondescription = {} exec(fh.read(), {'CustomOption': CustomOption}, optiondescription) # pylint: disable=W0122 @@ -113,17 +115,20 @@ def launch_flattener(test_dir, for root in ['1', '2']: config.option(f'{root}.{key}').information.set('test_information', value) # + if not isdir(makedict_dir): + mkdir(makedict_dir) config_dict = dict(option_value(config.value.get())) - if filename == 'base': - if not isdir(makedict_dir): - mkdir(makedict_dir) - if not isfile(makedict_file) or debug: - with open(makedict_file, 'w') as fh: - dump(config_dict, fh, indent=4) - fh.write('\n') - elif filename == 'no_namespace': - config_dict = {f'rougail.{path}': value for path, value in config_dict.items()} + if not isfile(Path(test_dir) / 'tiramisu' / 'base.py'): + tconfig_dict = {f'rougail.{path}': value for path, value in config_dict.items()} else: + tconfig_dict = config_dict + if not isfile(makedict_file) or debug: + with open(makedict_file, 'w') as fh: + dump(tconfig_dict, fh, indent=4) + fh.write('\n') + if filename == 'no_namespace': + config_dict = {f'rougail.{path}': value for path, value in config_dict.items()} + elif filename != 'base': config_dict_prefix = {'1': {}, '2': {}} for key, value in config_dict.items(): prefix, path = key.split('.', 1) @@ -142,9 +147,10 @@ def launch_flattener(test_dir, if not isfile(makedict_file): raise Exception('dict is not empty') with open(makedict_file, 'r') as fh: - assert load(fh) == loads(dumps(config_dict)), f"error in file {makedict_file}" + loaded_config_dict = load(fh) + assert loaded_config_dict == loads(dumps(config_dict)), f"error in file {makedict_file}" # - value_owner(makedict_before, config, filename) + value_owner(test_dir, makedict_before, config, filename) # deploy ro = config.property.default('read_only', 'append') ro = frozenset(list(ro) + ['force_store_value']) @@ -154,12 +160,12 @@ def launch_flattener(test_dir, config.property.setdefault(rw, 'read_write', 'append') config.property.add('force_store_value') # - value_owner(makedict_after, config, filename) + value_owner(test_dir, makedict_after, config, filename) # - mandatory(mandatory_file, config.value.mandatory(), filename) + mandatory(test_dir, mandatory_file, config.value.mandatory(), filename) -def value_owner(makedict_value_owner, config, filename): +def value_owner(test_dir, makedict_value_owner, config, filename): ret = {} for key, value in option_value(config.value.get(), True): path = key.path() @@ -182,14 +188,17 @@ def value_owner(makedict_value_owner, config, filename): ret[path] = {'owner': owner, 'value': value, } - if filename == 'base': - if not isfile(makedict_value_owner) or debug: - with open(makedict_value_owner, 'w') as fh: - dump(ret, fh, indent=4) - fh.write('\n') - elif filename == 'no_namespace': - ret = {f'rougail.{path}': value for path, value in ret.items()} + if not isfile(Path(test_dir) / 'tiramisu' / 'base.py'): + tret = {f'rougail.{path}': value for path, value in ret.items()} else: + tret = ret + if not isfile(makedict_value_owner) or debug: + with open(makedict_value_owner, 'w') as fh: + dump(tret, fh, indent=4) + fh.write('\n') + if filename == 'no_namespace': + ret = {f'rougail.{path}': value for path, value in ret.items()} + elif filename != 'base': ret_prefix = {'1': {}, '2': {}} for key, value in ret.items(): prefix, path = key.split('.', 1) @@ -200,14 +209,15 @@ def value_owner(makedict_value_owner, config, filename): assert load(fh) == loads(dumps(ret)), f"error in file {makedict_value_owner}" -def mandatory(mandatory_file, mandatories, filename): - ret = [opt.path() for opt in mandatories] +def mandatory(test_dir, mandatory_file, mandatories, filename): + if filename == 'no_namespace': + ret = [f'rougail.{opt.path()}' for opt in mandatories] + else: + ret = [opt.path() for opt in mandatories] if not mandatory_file.is_file(): with mandatory_file.open('w') as fh: dump(ret, fh) - if filename == 'no_namespace': - ret = [f'rougail.{path}' for path in ret] - elif filename == 'multi': + if filename == 'multi': ret_prefix = {'1': [], '2': []} for key in ret: prefix, path = key.split('.', 1) @@ -223,12 +233,14 @@ def test_dictionary(test_dir): print('FIXME') return test_dir = join(dico_dirs, test_dir) + if not (Path(test_dir) / 'tiramisu' / 'base.py').is_file(): + return launch_flattener(test_dir, 'base') def test_dictionary_no_namespace(test_dir): test_dir = join(dico_dirs, test_dir) - if not isfile(Path(test_dir) / 'tiramisu' / 'no_namespace.py'): + if not (Path(test_dir) / 'tiramisu' / 'no_namespace.py').is_file(): return launch_flattener(test_dir, 'no_namespace') @@ -238,6 +250,6 @@ def test_dictionary_multi(test_dir): print('FIXME') return test_dir = join(dico_dirs, test_dir) - if not isfile(Path(test_dir) / 'tiramisu' / 'multi.py'): + if not (Path(test_dir) / 'tiramisu' / 'multi.py').is_file(): return launch_flattener(test_dir, 'multi')