diff --git a/src/rougail/output_formatter/__init__.py b/src/rougail/output_formatter/__init__.py index d388cb0..2c4812d 100644 --- a/src/rougail/output_formatter/__init__.py +++ b/src/rougail/output_formatter/__init__.py @@ -1,6 +1,6 @@ """ Silique (https://www.silique.fr) -Copyright (C) 2024-2025 +Copyright (C) 2024-2026 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 @@ -119,7 +119,6 @@ class RougailOutputFormatter: _('the "step.output" is not set to "{0}"').format(self.output_name) ) # yaml.top_level_colon_align = True - self.main_namespace = normalize_family(self.rougailconfig["main_namespace"]) self.has_default_structural_format_version = ( self.rougailconfig["default_structural_format_version"] is not None ) @@ -135,7 +134,7 @@ class RougailOutputFormatter: def run(self): self.upgrade() - self.families = {self.main_namespace: CommentedMap()} + self.families = {self.rougail.namespace: CommentedMap()} self.parse() self.yaml.indent(mapping=2, sequence=4, offset=2) self.yaml.version = "1.2" @@ -143,7 +142,7 @@ class RougailOutputFormatter: self.yaml.explicit_end = True self.default_flow_style = False with BytesIO() as ymlfh: - families = self.families[self.main_namespace] + families = self.families[self.rougail.namespace] if not families: self.yaml.dump("", ymlfh) else: @@ -162,7 +161,14 @@ class RougailOutputFormatter: return self.attributes[type_name] def upgrade(self) -> None: - filenames = self.rougailconfig["main_structural_directories"] + namespace = self.rougailconfig["main_namespace"] + if namespace and self.rougailconfig["extra_namespaces.names"]: + namespace = self.rougailconfig["extra_namespaces.names"][0] + filenames = self.rougailconfig["extra_namespaces"]["directories"][0] + extra = True + else: + filenames = self.rougailconfig["main_structural_directories"] + extra = False if len(filenames) > 1: raise ExtensionError(_('only one filename is allowed, not "{0}"').format(filenames)) filename = Path(filenames[0]) @@ -177,11 +183,11 @@ class RougailOutputFormatter: self.rougail.load_config() self.rougail.init() self.filename_str = str(filename) - if self.main_namespace is None: + if namespace is None: self.rougail.namespace = None else: - self.rougail.namespace = normalize_family(self.main_namespace) - self.rougail.create_namespace(self.main_namespace) + self.rougail.namespace = normalize_family(namespace) + self.rougail.create_namespace(namespace) self.rougail.validate_file_version( datas, self.filename_str, @@ -199,26 +205,25 @@ class RougailOutputFormatter: return ret def parse(self): - self.families[self.main_namespace][self.version_name] = float( + self.families[self.rougail.namespace][self.version_name] = float( self.rougail.version ) self.remaining = len(self.rougail.paths._data) for path, obj in self.rougail.paths._data.items(): self.remaining -= 1 if path == self.rougail.namespace: - # self.families[path] = self.families[None] continue if isinstance(obj, Family): self.parse_family(path, obj) elif isinstance(obj, Variable): self.parse_variable(path, obj) - if list(self.families[self.main_namespace]) != [self.version_name]: + if list(self.families[self.rougail.namespace]) != [self.version_name]: # just to add an empty line space after "version" - self.families[self.main_namespace].yaml_value_comment_extend( + self.families[self.rougail.namespace].yaml_value_comment_extend( self.version_name, [CommentToken("\n\n", CommentMark(0)), None] ) if self.has_default_structural_format_version: - del self.families[self.main_namespace][self.version_name] + del self.families[self.rougail.namespace][self.version_name] def parse_family(self, path, obj): children = [p.rsplit(".", 1)[-1] for p in self.rougail.parents[path]] @@ -355,6 +360,8 @@ class RougailOutputFormatter: if attr not in force_keys and value == default_value: continue value = self.object_to_yaml(attr, type_, value, multi, path) + if isinstance(value, dict) and "identifier" in value: + value["identifier"] = self.object_to_yaml("identifier", type_, value["identifier"], True, path) variable[attr] = value if variable.get("mandatory") is True and None not in variable.get( "choices", [] @@ -605,7 +612,7 @@ class RougailOutputFormatter: def calc_variable_path(self, object_path, variable_path): if not variable_path.startswith("_"): common_path = get_common_path(object_path, variable_path) - if not self.rougail.namespace or common_path: + if not self.rougail.namespace: if not common_path: len_common_path = 0 else: @@ -617,6 +624,15 @@ class RougailOutputFormatter: + "." + variable_path[len_common_path:] ) + if common_path: + len_common_path = len(common_path) + 1 + relative_object_path = object_path[len_common_path:] + final_path = "_" * (relative_object_path.count(".") + 1) + "." + return ( + "_" * (relative_object_path.count(".") + 1) + + "." + + variable_path[len_common_path:] + ) return variable_path def get_parent_name(self, path): @@ -636,7 +652,7 @@ class RougailOutputFormatter: name = search_name return _yaml(y[name]) - if self.main_namespace: + if self.rougail.namespace: subpath = path.split(".")[1:] else: subpath = path.split(".") diff --git a/src/rougail/output_formatter/config.py b/src/rougail/output_formatter/config.py index fa9b42b..e9b9f78 100644 --- a/src/rougail/output_formatter/config.py +++ b/src/rougail/output_formatter/config.py @@ -1,6 +1,6 @@ """ Silique (https://www.silique.fr) -Copyright (C) 2024-2025 +Copyright (C) 2024-2026 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 diff --git a/src/rougail/output_formatter/i18n.py b/src/rougail/output_formatter/i18n.py index 3b8fe6f..612295e 100644 --- a/src/rougail/output_formatter/i18n.py +++ b/src/rougail/output_formatter/i18n.py @@ -1,6 +1,6 @@ """Internationalisation utilities Silique (https://www.silique.fr) -Copyright (C) 2025 +Copyright (C) 2025-2026 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 diff --git a/src/rougail/output_formatter/upgrade.py b/src/rougail/output_formatter/upgrade.py index 61b4dc7..35e3031 100644 --- a/src/rougail/output_formatter/upgrade.py +++ b/src/rougail/output_formatter/upgrade.py @@ -4,7 +4,7 @@ Cadoles (http://www.cadoles.com) Copyright (C) 2021 Silique (https://www.silique.fr) -Copyright (C) 2022-2025 +Copyright (C) 2022-2026 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 diff --git a/tests/results/00_2default_calculated_variable_description/rougail/00-base.yml b/tests/results/00_2default_calculated_variable_description/rougail/00-base.yml index f5948c3..86bee89 100644 --- a/tests/results/00_2default_calculated_variable_description/rougail/00-base.yml +++ b/tests/results/00_2default_calculated_variable_description/rougail/00-base.yml @@ -7,6 +7,6 @@ var1: # a first variable var2: description: a second variable default: - variable: _.var1 description: value of a variable! + variable: _.var1 ... diff --git a/tests/results/00_2default_calculated_variable_description_multi_line/rougail/00-base.yml b/tests/results/00_2default_calculated_variable_description_multi_line/rougail/00-base.yml index 4cc8223..e00884c 100644 --- a/tests/results/00_2default_calculated_variable_description_multi_line/rougail/00-base.yml +++ b/tests/results/00_2default_calculated_variable_description_multi_line/rougail/00-base.yml @@ -7,12 +7,12 @@ var1: # a first variable var2: description: a second variable default: - variable: _.var1 description: |- value of a variable! + variable: _.var1 var3: # a new variable ... diff --git a/tests/results/00_6ip/rougail/00-base.yml b/tests/results/00_6ip/rougail/00-base.yml index 3322b58..31bba05 100644 --- a/tests/results/00_6ip/rougail/00-base.yml +++ b/tests/results/00_6ip/rougail/00-base.yml @@ -10,7 +10,7 @@ var1: var2: description: an IP in CIDR format examples: - - 192.168.0.128/25 + - 192.168.0.129/25 type: ip params: cidr: true diff --git a/tests/results/00_6secret_param/rougail/00-base.yml b/tests/results/00_6secret_param/rougail/00-base.yml index dd2851c..1307471 100644 --- a/tests/results/00_6secret_param/rougail/00-base.yml +++ b/tests/results/00_6secret_param/rougail/00-base.yml @@ -4,6 +4,8 @@ version: 1.1 secret1: description: the first variable + examples: + - ALongS4cr4t type: secret params: min_len: 10 diff --git a/tests/results/04_1jinja_and_hidden/rougail/00-base.yml b/tests/results/04_1jinja_and_hidden/rougail/00-base.yml new file mode 100644 index 0000000..2cac7f6 --- /dev/null +++ b/tests/results/04_1jinja_and_hidden/rougail/00-base.yml @@ -0,0 +1,18 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: + description: A first variable + type: boolean + default: + jinja: >- + true + description: A description + hidden: true + +var2: + description: A second variable + disabled: + variable: _.var1 +... diff --git a/tests/results/04_5disabled_calculation_variable11/rougail/00-base.yml b/tests/results/04_5disabled_calculation_variable11/rougail/00-base.yml new file mode 100644 index 0000000..d1054f1 --- /dev/null +++ b/tests/results/04_5disabled_calculation_variable11/rougail/00-base.yml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +version: 1.1 + +condition: + description: a condition + default: true + disabled: true + +variable: + description: a variable + disabled: + variable: _.condition + propertyerror: transitive +... diff --git a/tests/results/04_5disabled_calculation_variable_transitive/rougail/00-base.yml b/tests/results/04_5disabled_calculation_variable_transitive/rougail/00-base.yml new file mode 100644 index 0000000..150c4d1 --- /dev/null +++ b/tests/results/04_5disabled_calculation_variable_transitive/rougail/00-base.yml @@ -0,0 +1,17 @@ +%YAML 1.2 +--- +version: 1.1 + +condition: true # a condition + +variable1: + description: a variable + disabled: + variable: _.condition + +variable2: + description: a second variable + disabled: + variable: _.variable1 + propertyerror: transitive +... diff --git a/tests/results/04_5disabled_calculation_variable_transitive_2/rougail/00-base.yml b/tests/results/04_5disabled_calculation_variable_transitive_2/rougail/00-base.yml new file mode 100644 index 0000000..d1054f1 --- /dev/null +++ b/tests/results/04_5disabled_calculation_variable_transitive_2/rougail/00-base.yml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +version: 1.1 + +condition: + description: a condition + default: true + disabled: true + +variable: + description: a variable + disabled: + variable: _.condition + propertyerror: transitive +... diff --git a/tests/results/04_5disabled_calculation_variable_transitive_3/rougail/00-base.yml b/tests/results/04_5disabled_calculation_variable_transitive_3/rougail/00-base.yml new file mode 100644 index 0000000..8595d92 --- /dev/null +++ b/tests/results/04_5disabled_calculation_variable_transitive_3/rougail/00-base.yml @@ -0,0 +1,19 @@ +%YAML 1.2 +--- +version: 1.1 + +condition: false # a condition + +variable1: + description: a variable + default: disabled + disabled: + variable: _.condition + +variable2: + description: a second variable + disabled: + variable: _.variable1 + propertyerror: transitive + when: disabled +... diff --git a/tests/results/04_5disabled_calculation_variable_transitive_4/rougail/00-base.yml b/tests/results/04_5disabled_calculation_variable_transitive_4/rougail/00-base.yml new file mode 100644 index 0000000..a452f7d --- /dev/null +++ b/tests/results/04_5disabled_calculation_variable_transitive_4/rougail/00-base.yml @@ -0,0 +1,19 @@ +%YAML 1.2 +--- +version: 1.1 + +condition: true # a condition + +variable1: + description: a variable + default: not_disabled + disabled: + variable: _.condition + +variable2: + description: a second variable + disabled: + variable: _.variable1 + propertyerror: transitive + when: disabled +... diff --git a/tests/results/40_0leadership_reduce/rougail/00-base.yml b/tests/results/40_0leadership_reduce/rougail/00-base.yml index 204e7b8..d41716a 100644 --- a/tests/results/40_0leadership_reduce/rougail/00-base.yml +++ b/tests/results/40_0leadership_reduce/rougail/00-base.yml @@ -8,7 +8,7 @@ leadership: leader: description: a leader - test: + examples: - val1 - val2 default: diff --git a/tests/results/60_0family_dynamic_jinja_integer_empty2/rougail/00-base.yml b/tests/results/60_0family_dynamic_jinja_integer_empty2/rougail/00-base.yml new file mode 100644 index 0000000..2fab042 --- /dev/null +++ b/tests/results/60_0family_dynamic_jinja_integer_empty2/rougail/00-base.yml @@ -0,0 +1,29 @@ +%YAML 1.2 +--- +version: 1.1 + +var: + description: a suffix variable + test: + - 1 + - 2 + type: integer + multi: true + mandatory: false + +dyn{{ identifier }}: + description: a dynamic family + dynamic: + variable: _.var + + var: val # a variable inside dynamic family from "{{ identifier }}" + +var2: + description: a variable + default: + jinja: >- + {% if rougail.dyn1 is defined %} + {{ rougail.dyn1.var }} + {% endif %} + description: get the value of "rougail.dyn1.var" +... diff --git a/tests/results/60_5family_dynamic_calc_description/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_description/rougail/00-base.yml index facee7b..f3fdd62 100644 --- a/tests/results/60_5family_dynamic_calc_description/rougail/00-base.yml +++ b/tests/results/60_5family_dynamic_calc_description/rougail/00-base.yml @@ -3,21 +3,21 @@ version: 1.1 dyn{{ identifier }}: - description: A dynamic famify for {{ identifier }} + description: a dynamic famify for {{ identifier }} dynamic: - val1 - val2 - var: # A dynamic variable for {{ identifier }} + var: # a dynamic variable for {{ identifier }} var1: - description: A new variable + description: a new variable disabled: variable: _.dynval1.var when: val var2: - description: A new variable + description: a new variable default: variable: _.dyn{{ identifier }}.var unique: false diff --git a/tests/results/60_5family_dynamic_calc_identifier/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_identifier/rougail/00-base.yml new file mode 100644 index 0000000..5990218 --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_identifier/rougail/00-base.yml @@ -0,0 +1,26 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: # A suffix variable + - val1 + - val2 + +var2: val1 # A suffix variable2 + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: + description: A dynamic variable + default: + type: identifier + +var3: + description: A variable calculated + default: + variable: _.dyn{{ identifier }}.var + identifier: + variable: _.var2 +... diff --git a/tests/results/60_5family_dynamic_calc_identifier_multi/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_identifier_multi/rougail/00-base.yml new file mode 100644 index 0000000..83fae25 --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_identifier_multi/rougail/00-base.yml @@ -0,0 +1,24 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: # A suffix variable + - val1 + - val2 + +var2: val1 # A suffix variable2 + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: # A dynamic variable + - type: identifier + +var3: + description: A variable calculated + default: + variable: _.dyn{{ identifier }}.var + identifier: + variable: _.var2 +... diff --git a/tests/results/60_5family_dynamic_calc_suffix_disabled2/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_suffix_disabled2/rougail/00-base.yml index c9ec105..1e9ef27 100644 --- a/tests/results/60_5family_dynamic_calc_suffix_disabled2/rougail/00-base.yml +++ b/tests/results/60_5family_dynamic_calc_suffix_disabled2/rougail/00-base.yml @@ -7,7 +7,8 @@ var1: test: - val1 - val2 - multi: true + default: + - val1 mandatory: false dyn{{ identifier }}: @@ -22,5 +23,5 @@ var2: description: A variable calculated default: variable: _.dynval1.var - optional: true + propertyerror: false ... diff --git a/tests/results/60_5family_dynamic_calc_suffix_disabled3/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_suffix_disabled3/rougail/00-base.yml new file mode 100644 index 0000000..c9ec105 --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_suffix_disabled3/rougail/00-base.yml @@ -0,0 +1,26 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: + description: A suffix variable + test: + - val1 + - val2 + multi: true + mandatory: false + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: + description: A dynamic variable + disabled: true + +var2: + description: A variable calculated + default: + variable: _.dynval1.var + optional: true +... diff --git a/tests/results/60_5family_dynamic_unknown_suffix/rougail/00-base.yml b/tests/results/60_5family_dynamic_unknown_suffix/rougail/00-base.yml index 2a5226f..74276d6 100644 --- a/tests/results/60_5family_dynamic_unknown_suffix/rougail/00-base.yml +++ b/tests/results/60_5family_dynamic_unknown_suffix/rougail/00-base.yml @@ -4,7 +4,7 @@ version: 1.1 var: description: a suffix variable - test: + examples: - val1 - val2 - val3 diff --git a/tests/results/60_6family_dynamic_inside_identifier/rougail/00-base.yml b/tests/results/60_6family_dynamic_inside_identifier/rougail/00-base.yml new file mode 100644 index 0000000..0ce9ffe --- /dev/null +++ b/tests/results/60_6family_dynamic_inside_identifier/rougail/00-base.yml @@ -0,0 +1,33 @@ +%YAML 1.2 +--- +version: 1.1 + +var: # a suffix variable + - val1 + - val2 + +'{{ identifier }}_dyn': + description: a dynamic family + dynamic: + variable: _.var + + var1: + description: value is suffix for {{ identifier }} + default: + type: identifier + + var2: + description: value is first variable + default: + variable: _.var1 + + var3: + description: value is relative first variable + default: + variable: _.var1 + + var4: + description: value is first variable of val1 + default: + variable: __.val1_dyn.var1 +... diff --git a/tests/results/60_6family_subdynamic_inside/rougail/00-base.yml b/tests/results/60_6family_subdynamic_inside/rougail/00-base.yml new file mode 100644 index 0000000..794f037 --- /dev/null +++ b/tests/results/60_6family_subdynamic_inside/rougail/00-base.yml @@ -0,0 +1,24 @@ +%YAML 1.2 +--- +version: 1.1 + +var: # A suffix variable + - val1 + - val2 + +dyn{{ identifier }}: + dynamic: + variable: _.var + + dyn{{ identifier }}: + dynamic: + variable: __.var + + var1: # A dynamic variable + + var2: + description: A variable calculated + default: + variable: ___.dynval2.dyn{{ identifier }}.var1 + unique: false +... diff --git a/tests/test_load.py b/tests/test_load.py index 83bcece..8a6b01a 100644 --- a/tests/test_load.py +++ b/tests/test_load.py @@ -68,18 +68,26 @@ def test_structural_files_formatter_namespace(test_dir): rougailconfig = get_rougail_config(test_dir, namespace) if not rougailconfig: return + if rougailconfig["extra_namespaces.names"]: + for n_idx, namespace in enumerate(rougailconfig['extra_namespaces.names']): + for dir_name in rougailconfig["extra_namespaces"]["directories"][n_idx]: + for file_name in Path(dir_name).iterdir(): + if file_name.suffix in [".yml", ".yaml"]: + rougailconfig["extra_namespaces"].set_follower("directories", n_idx, [str(file_name)]) + _test_structural_files(file_name, namespace, rougailconfig) + rougailconfig["extra_namespaces"].reset() for dir_name in rougailconfig['main_structural_directories']: for file_name in Path(dir_name).iterdir(): if file_name.suffix in [".yml", ".yaml"]: rougailconfig["main_structural_directories"] = [str(file_name)] _test_structural_files(file_name, namespace, rougailconfig) - for namespace, dirs_name in rougailconfig['extra_namespaces'].items(): - rougailconfig["main_namespace"] = namespace - for dir_name in dirs_name: - for file_name in Path(dir_name).iterdir(): - if file_name.suffix in [".yml", ".yaml"]: - rougailconfig["main_structural_directories"] = [str(file_name)] - _test_structural_files(file_name, namespace, rougailconfig) +# for namespace, dirs_name in rougailconfig['extra_namespaces'].items(): +# rougailconfig["main_namespace"] = namespace +# for dir_name in dirs_name: +# for file_name in Path(dir_name).iterdir(): +# if file_name.suffix in [".yml", ".yaml"]: +# rougailconfig["main_structural_directories"] = [str(file_name)] +# _test_structural_files(file_name, namespace, rougailconfig) def test_structural_files_formatter_load(test_dir):