diff --git a/src/rougail/output_formatter/__init__.py b/src/rougail/output_formatter/__init__.py index d97362e..fe9b763 100644 --- a/src/rougail/output_formatter/__init__.py +++ b/src/rougail/output_formatter/__init__.py @@ -31,12 +31,11 @@ from ruamel.yaml.scalarstring import LiteralScalarString, FoldedScalarString, Sc from djlint.settings import Config from djlint.reformat import formatter -from tiramisu import undefined from tiramisu.config import get_common_path from rougail.convert import RougailConvert from rougail.object_model import Variable, Family, Calculation, JinjaCalculation, IdentifierCalculation, IdentifierPropertyCalculation, NamespaceCalculation, IdentifierParam, IndexCalculation, IndexParam, NamespaceParam, Param -from rougail.utils import normalize_family +from rougail.utils import normalize_family, undefined from .upgrade import RougailUpgrade from .__version__ import __version__ @@ -82,25 +81,57 @@ class RougailOutputFormatter: from rougail import RougailConfig rougailconfig = RougailConfig rougailconfig["step.output"] = self.output_name - if rougailconfig["step.output"] != self.output_name: + self.rougailconfig = rougailconfig + if self.rougailconfig["step.output"] != self.output_name: raise ExtentionError(_('the "step.output" is not set to "{0}"').format(self.output_name)) # yaml.top_level_colon_align = True - self.main_namespace = rougailconfig["main_namespace"] - filenames = rougailconfig["main_dictionaries"] - if len(rougailconfig["main_dictionaries"]) > 1: + self.main_namespace = normalize_family(self.rougailconfig["main_namespace"]) + self.has_default_dictionary_format_version = self.rougailconfig["default_dictionary_format_version"] is not None + self.config = Config() + self.config.profile = 'jinja' + self.config.line_break_after_multiline_tag = True + self.config.indent = " " + self.attributes = {} + + self.yaml = YAML() + + def run(self): + self.upgrade() + self.families = {self.main_namespace: CommentedMap()} + self.parse() + self.yaml.indent(mapping=2, sequence=4, offset=2) + self.yaml.version = '1.2' + self.yaml.explicit_start = True + self.yaml.explicit_end = True + self.default_flow_style = False + with BytesIO() as ymlfh: + families = self.families[self.main_namespace] + if not families: + self.yaml.dump('', ymlfh) + else: + self.yaml.dump(families, ymlfh) + ret = ymlfh.getvalue().decode("utf-8").strip() + return True, ret + + def get_attributes(self, obj, excludes=[]) -> dict: + type_name = obj.__name__ + if type_name == 'Variable' and excludes == []: + raise Exception('pff') + if type_name not in self.attributes: + self.attributes[type_name] = {str(attr): o.default for attr, o in obj.model_fields.items() if str(attr) not in excludes} + return self.attributes[type_name] + + def upgrade(self) -> None: + filenames = self.rougailconfig["main_dictionaries"] + if len(filenames) > 1: raise Exception(_('only one file is allowed')) filename = Path(filenames[0]) if not filename.is_file(): raise Exception(_('only a file is allowed')) - self.config = Config() - self.config.profile = 'jinja' - self.config.line_break_after_multiline_tag = True - self.config.indent = " " - - self.original_yaml = RougailUpgrade(rougailconfig).run(filename) - datas = RougailUpgrade(rougailconfig).run(filename) - self.rougail = RougailConvert(rougailconfig) + self.version_name, self.original_yaml = RougailUpgrade(self.rougailconfig).run(filename) + self.version_name, datas = RougailUpgrade(self.rougailconfig).run(filename) + self.rougail = RougailConvert(self.rougailconfig) self.rougail.load_config() self.rougail.init() self.filename_str = str(filename) @@ -119,23 +150,6 @@ class RougailOutputFormatter: "1.1", datas, ) - self.yaml = YAML() - - def run(self): - self.families_attributes = {attr: obj.get("default") for attr, obj in self.rougail.family.model_json_schema()["properties"].items()} - self.dynamics_attributes = {attr: obj.get("default") for attr, obj in self.rougail.dynamic.model_json_schema()["properties"].items()} - self.variables_attributes = {attr: obj.get("default") for attr, obj in self.rougail.variable.model_json_schema()["properties"].items()} - self.families = {None: CommentedMap()} - self.parse() - self.yaml.indent(mapping=2, sequence=4, offset=2) - self.yaml.version = '1.2' - self.yaml.explicit_start = True - self.yaml.explicit_end = True - self.default_flow_style = False - with BytesIO() as ymlfh: - self.yaml.dump(self.families[None], ymlfh) - ret = ymlfh.getvalue().decode("utf-8").strip() - return True, ret def print(self): ret, data = self.run() @@ -143,33 +157,21 @@ class RougailOutputFormatter: return ret def parse(self): - # FIXME path to relative ! - if self.rougail.namespace: - version_path = f'{self.rougail.namespace}.version' - else: - version_path = 'version' - if version_path in self.rougail.paths._data: - version_name = '_version' - else: - version_name = 'version' - self.families[None][version_name] = None - self.families[None].yaml_value_comment_extend(version_name, [CommentToken('\n\n', CommentMark(0)), None]) - version = None + self.families[self.main_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 version is None or version == '': - version = obj.version if path == self.rougail.namespace: - self.families[path] = self.families[None] +# self.families[path] = self.families[None] continue if isinstance(obj, Family): self.parse_family(path, obj) if isinstance(obj, Variable): self.parse_variable(path, obj) - if not version: - raise Exception(_(f'no variables in file {self.filename_str}')) - self.families[None][version_name] = float(version) + if list(self.families[self.main_namespace]) != [self.version_name]: + self.families[self.main_namespace].yaml_value_comment_extend(self.version_name, [CommentToken('\n\n', CommentMark(0)), None]) + if self.has_default_dictionary_format_version: + del self.families[self.main_namespace][self.version_name] def parse_family(self, path, obj): children = [p.rsplit('.', 1)[-1] for p in self.rougail.parents[path]] @@ -187,9 +189,9 @@ class RougailOutputFormatter: force_keys = list(yaml_data) type_ = obj.type if type_ == "dynamic": - attributes = self.dynamics_attributes + attributes = self.get_attributes(self.rougail.dynamic) else: - attributes = self.families_attributes + attributes = self.get_attributes(self.rougail.family) for attr, default_value in attributes.items(): if attr in ["name", "path", "namespace", "version", "xmlfiles"]: continue @@ -244,9 +246,7 @@ class RougailOutputFormatter: force_keys = list(yaml_data) multi = obj.multi or isinstance(obj.default, list) type_ = obj.type - for attr, default_value in self.variables_attributes.items(): - if attr in ["name", "path", "namespace", "version", "xmlfiles"]: - continue + for attr, default_value in self.get_attributes(self.rougail.variable, ["name", "path", "namespace", "version", "xmlfiles"]).items(): try: value = getattr(obj, attr) except AttributeError: @@ -372,12 +372,15 @@ class RougailOutputFormatter: if value.return_type: jinja["return_type"] = value.return_type if value.description: - jinja["description"] = value.description + if '\n' in value.description: + jinja["description"] = LiteralScalarString(value.description) + else: + jinja["description"] = value.description if value.params: jinja["params"] = self.object_to_yaml("params", type_, value.params, multi, object_path) return jinja elif isinstance(value, Calculation): - variable_attributes = self.get_object_informations(value, ['path', 'inside_list', 'version', 'xmlfiles', 'attribute_name', 'namespace']) + variable_attributes = self.get_attributes(value.__class__, ['path', 'inside_list', 'version', 'xmlfiles', 'attribute_name', 'namespace']) variable = CommentedMap() if isinstance(value, (IdentifierCalculation, IdentifierPropertyCalculation)): variable["type"] = "identifier" @@ -393,9 +396,14 @@ class RougailOutputFormatter: variable["variable"] = self.calc_variable_path(object_path, variable["variable"]) if variable.get('type') == 'identifier' and 'identifier' in variable: del variable["type"] + if value.description: + if '\n' in value.description: + variable["description"] = LiteralScalarString(value.description) + else: + variable["description"] = value.description return variable elif isinstance(value, Param): - param_attributes = self.get_object_informations(value, ["type", "key", "namespace"]) + param_attributes = self.get_attributes(value.__class__, ["type", "key", "namespace"]) if list(param_attributes) == ['value']: variable = value.value else: @@ -434,9 +442,6 @@ class RougailOutputFormatter: return "_" * (relative_object_path.count(".") + 1) + '.' + variable_path[len_common_path:] return variable_path - def get_object_informations(self, value, excludes=[]): - return {attr: obj.get("default") for attr, obj in value.__class__.model_json_schema()["properties"].items() if attr not in excludes} - def get_parent_name(self, path): if "." in path: return path.rsplit(".", 1) diff --git a/src/rougail/output_formatter/upgrade.py b/src/rougail/output_formatter/upgrade.py index f44c9f0..17c7f19 100644 --- a/src/rougail/output_formatter/upgrade.py +++ b/src/rougail/output_formatter/upgrade.py @@ -45,22 +45,33 @@ class RougailUpgrade: def run( self, - file, - ): + file: str, + ) -> dict: with file.open() as file_fh: root = YAML().load(file_fh) - search_function_name = get_function_name(str(root["version"])) + if not root: + root = {} + if '_version' in root: + version_name = '_version' + format_version = str(root.pop('_version')) + elif 'version' in root: + version_name = 'version' + format_version = str(root.pop('version')) + else: + version_name = None + format_version = self.rougailconfig["default_dictionary_format_version"] + if format_version not in VERSIONS: + raise Exception(f'version "{format_version}" is not a valid version') + search_function_name = get_function_name(format_version) function_found = False for version, function_version in FUNCTION_VERSIONS: if function_found and hasattr(self, function_version): root = getattr(self, function_version)(root) if function_version == search_function_name: function_found = True - if '_version' in root: - root["_version"] = float(version) - elif 'version' in root: - root["version"] = float(version) - return root + if version_name: + root[version_name] = float(version) + return version_name, root def update_1_1( self, diff --git a/tests/results/00_0no_variable/rougail/00-base.yml b/tests/results/00_0no_variable/rougail/00-base.yml new file mode 100644 index 0000000..94a82bd --- /dev/null +++ b/tests/results/00_0no_variable/rougail/00-base.yml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +version: 1.1 +... diff --git a/tests/results/00_0no_variable_default_version/rougail/00-base.yml b/tests/results/00_0no_variable_default_version/rougail/00-base.yml new file mode 100644 index 0000000..ee52480 --- /dev/null +++ b/tests/results/00_0no_variable_default_version/rougail/00-base.yml @@ -0,0 +1,3 @@ +%YAML 1.2 +--- +... diff --git a/tests/results/00_0no_variable_remove_version/rougail/00-base.yml b/tests/results/00_0no_variable_remove_version/rougail/00-base.yml new file mode 100644 index 0000000..ee52480 --- /dev/null +++ b/tests/results/00_0no_variable_remove_version/rougail/00-base.yml @@ -0,0 +1,3 @@ +%YAML 1.2 +--- +... diff --git a/tests/results/00_2default_calculated_params_permissive/rougail/00-base.yml b/tests/results/00_2default_calculated_params_permissive/rougail/00-base.yml new file mode 100644 index 0000000..b389034 --- /dev/null +++ b/tests/results/00_2default_calculated_params_permissive/rougail/00-base.yml @@ -0,0 +1,22 @@ +%YAML 1.2 +--- +version: 1.1 + +leadership: + type: leadership + hidden: true + + var1: # a first variable + - a_value + + var2: a_value # a first variable + +var2: + description: a second variable + default: + jinja: >- + {{ var1[0] }} + params: + var1: + variable: _.leadership.var1 +... 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 new file mode 100644 index 0000000..f5948c3 --- /dev/null +++ b/tests/results/00_2default_calculated_variable_description/rougail/00-base.yml @@ -0,0 +1,12 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: # a first variable + +var2: + description: a second variable + default: + variable: _.var1 + description: value of a variable! +... diff --git a/tests/results/40_6leadership_follower_multi_no_mandatory/rougail/00-base.yml b/tests/results/40_6leadership_follower_multi_no_mandatory/rougail/00-base.yml new file mode 100644 index 0000000..c6e1353 --- /dev/null +++ b/tests/results/40_6leadership_follower_multi_no_mandatory/rougail/00-base.yml @@ -0,0 +1,18 @@ +%YAML 1.2 +--- +version: 1.1 + +leadership: + description: A leadership + type: leadership + + leader: + description: The leader + multi: true + mandatory: false + + follower1: [] # The first follower + + follower2: # The second follower + - value +... diff --git a/tests/results/60_5family_dynamic_calc_suffix_hidden/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_suffix_hidden/rougail/00-base.yml new file mode 100644 index 0000000..b5ea488 --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_suffix_hidden/rougail/00-base.yml @@ -0,0 +1,25 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: + description: A suffix variable + default: + - val1 + - val2 + mandatory: false + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: + description: A dynamic variable + default: a value + hidden: true + +var2: + description: A variable calculated + default: + variable: _.dynval1.var +... diff --git a/tests/results/60_5family_dynamic_calc_suffix_hidden_boolean/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_suffix_hidden_boolean/rougail/00-base.yml new file mode 100644 index 0000000..510b4d9 --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_suffix_hidden_boolean/rougail/00-base.yml @@ -0,0 +1,25 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: + description: A suffix variable + default: + - val1 + - val2 + mandatory: false + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: + description: A dynamic variable + default: true + hidden: true + +var2: + description: A variable calculated + default: + variable: _.dynval1.var +... diff --git a/tests/results/60_5family_dynamic_calc_suffix_hidden_multi/rougail/00-base.yml b/tests/results/60_5family_dynamic_calc_suffix_hidden_multi/rougail/00-base.yml new file mode 100644 index 0000000..616d73f --- /dev/null +++ b/tests/results/60_5family_dynamic_calc_suffix_hidden_multi/rougail/00-base.yml @@ -0,0 +1,27 @@ +%YAML 1.2 +--- +version: 1.1 + +var1: + description: A suffix variable + default: + - val1 + - val2 + mandatory: false + +dyn{{ identifier }}: + dynamic: + variable: _.var1 + + var: + description: A dynamic variable + default: + - a value + - a second value + hidden: true + +var2: + description: A variable calculated + default: + variable: _.dynval1.var +...