Coverage for denofo/comparator/compare.py: 100%
69 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-09 16:33 +0200
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-09 16:33 +0200
1from pathlib import Path
2from enum import Enum
3from typing import Any
4from denofo.utils.constants import (
5 SUBMODELS,
6 INDENT_LVL_DICT,
7)
10def _turn_value_to_string(val: Any, model_name: str, field_name: str) -> str:
11 """
12 Turn an element into a string.
14 :param val: The element to turn into a string.
15 :type val: Any
16 :param model_name: The name of the model (necessary for correct indentation level).
17 :type model_name: str
18 :param field_name: The name of the field (necessary for correct indentation level).
19 :type field_name: str
20 :return: The element as a string.
21 :rtype: str
22 """
23 val_str = ""
24 leading_tab_num = max(
25 INDENT_LVL_DICT.get(field_name, 0), INDENT_LVL_DICT.get(model_name, 0)
26 )
28 if isinstance(val, dict):
29 val_str = "\n".join(
30 [
31 (
32 f"{(leading_tab_num + 1) * '\t'}{k}:\n"
33 f"{_turn_value_to_string(v, new_model, new_field)}"
34 )
35 for k, v in val.items()
36 if v is not None
37 and (new_model := k if k in SUBMODELS else model_name)
38 and (new_field := k if k not in SUBMODELS else field_name)
39 ]
40 )
41 elif isinstance(val, (list, tuple, set)):
42 val_str = "\n".join(
43 [_turn_value_to_string(e, model_name, field_name) for e in val]
44 )
45 elif isinstance(val, Enum):
46 val_str = f"{(leading_tab_num + 1) * '\t'}{val.value}"
47 else:
48 val_str = f"{(leading_tab_num + 1) * '\t'}{val}"
50 return val_str
53def _get_output_string(
54 comparison: list[tuple], mode: str, name1: str, name2: str
55) -> str:
56 """
57 Get the comparison result as a string.
59 :param comparison: The comparison input as preprocessed by :func:`denofo.utils.helpers.compare_two_models` .
60 :type comparison: list[tuple]
61 :param mode: The mode of comparison, either "similarities" or "differences".
62 :type mode: str
63 :param name1: The display name of the first comparison element.
64 :type name1: str
65 :param name2: The display name of the second comparison element.
66 :type name2: str
67 :return: The comparison result as a formatted string.
68 :rtype: str
69 """
70 last_model = ""
71 last_field = ""
72 last_comparison_type = ""
73 output_string = ""
74 tab = "\t"
75 passed_models = set()
77 if mode == "similarities":
78 output_string += f"Identical values between {name1} and {name2}:\n\n"
79 compare_lst = [elem for elem in comparison if elem[0] == "same"]
80 elif mode == "differences":
81 output_string += f"Differences between {name1} and {name2}:\n\n"
82 compare_lst = [elem for elem in comparison if elem[0] != "same"]
84 for elem in compare_lst:
85 prefix_string = False
86 comparison_type = elem[0]
87 model = elem[1]
88 field = elem[2]
89 val_lst = elem[3:]
91 if model != last_model:
92 if model not in passed_models and model in SUBMODELS:
93 output_string += f"{INDENT_LVL_DICT[model] * '\t'}{model}:\n"
94 passed_models.add(model)
96 last_model = model
97 last_field = ""
98 last_comparison_type = ""
100 if field != last_field:
101 output_string += f"{INDENT_LVL_DICT[field] * tab}{field}:\n"
102 last_field = field
103 last_comparison_type = ""
104 if comparison_type != last_comparison_type:
105 prefix_string = True
106 last_comparison_type = comparison_type
108 leading_tabs = (
109 max(INDENT_LVL_DICT.get(field, 0), INDENT_LVL_DICT.get(model, 0)) + 1
110 ) * tab
111 val_str = _turn_value_to_string(val_lst, model, field)
113 if mode == "similarities":
114 if comparison_type == "same":
115 output_string += f"{val_str}\n"
116 elif comparison_type == "diffval":
117 prefix_string = (
118 f"\n{leading_tabs}differing values in {name1} and {name2}:\n"
119 if prefix_string
120 else ""
121 )
122 output_string += f"{prefix_string}{val_str}\n\n"
123 elif comparison_type == "2not1":
124 prefix_string = (
125 f"\n{leading_tabs}values in {name2} but not in {name1}:\n"
126 if prefix_string
127 else ""
128 )
129 output_string += f"{prefix_string}{val_str}\n\n"
130 elif comparison_type == "1not2":
131 prefix_string = (
132 f"\n{leading_tabs}values in {name1} but not in {name2}:\n"
133 if prefix_string
134 else ""
135 )
136 output_string += f"{prefix_string}{val_str}\n\n"
138 return output_string.replace("\n\n\n", "\n\n").strip()
141def write_comparison(
142 comparison: list[tuple],
143 mode: str = "differences",
144 output_path: Path | None = None,
145 name1: str = "dngf_1",
146 name2: str = "dngf_2",
147) -> str | None:
148 """
149 Write the comparison result to the output file.
151 :param comparison: The comparison input as preprocessed by :func:`denofo.utils.helpers.compare_two_models` .
152 :type comparison: list[tuple]
153 :param mode: The mode of comparison, either "similarities" or "differences". Defaults to "differences".
154 :type mode: str
155 :param output_path: The path to the output file. If None, the result is returned as a string.
156 :type output_path: Path | None
157 :param name1: The display name of the first comparison element. Defaults to "dngf_1".
158 :type name1: str
159 :param name2: The display name of the second comparison element. Defaults to "dngf_2".
160 :type name2: str
161 :return: The comparison result as a string if output_path is None, otherwise None.
162 :rtype: str | None
163 """
164 output_str = _get_output_string(comparison, mode, name1, name2)
166 if output_path is not None:
167 with open(output_path, "w") as output_file:
168 output_file.write(output_str)
169 else:
170 return output_str