Skip to content

Commit 97c6a12

Browse files
authored
Fix: Sort nested AttributeDict's to prevent visual diff (#5397)
1 parent 131a4db commit 97c6a12

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

sqlmesh/utils/jinja.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ def extract_macro_references_and_variables(
206206
return macro_references, variables
207207

208208

209+
def sort_dict_recursive(
210+
item: t.Dict[str, t.Any],
211+
) -> t.Dict[str, t.Any]:
212+
sorted_dict: t.Dict[str, t.Any] = {}
213+
for k, v in sorted(item.items()):
214+
if isinstance(v, list):
215+
sorted_dict[k] = sorted(v)
216+
elif isinstance(v, dict):
217+
sorted_dict[k] = sort_dict_recursive(v)
218+
else:
219+
sorted_dict[k] = v
220+
return sorted_dict
221+
222+
209223
JinjaGlobalAttribute = t.Union[str, int, float, bool, AttributeDict]
210224

211225

@@ -440,7 +454,7 @@ def to_expressions(self) -> t.List[Expression]:
440454
d.PythonCode(
441455
expressions=[
442456
f"{k} = '{v}'" if isinstance(v, str) else f"{k} = {v}"
443-
for k, v in sorted(filtered_objs.items())
457+
for k, v in sort_dict_recursive(filtered_objs).items()
444458
]
445459
)
446460
)

tests/utils/test_jinja.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,30 @@ def test_dbt_adapter_macro_scope():
302302

303303
rendered = registry.build_environment().from_string("{{ spark__macro_a() }}").render()
304304
assert rendered.strip() == "macro_a"
305+
306+
307+
def test_macro_registry_to_expressions_sorted():
308+
refs = AttributeDict(
309+
{
310+
"payments": {
311+
"database": "jaffle_shop",
312+
"schema": "main",
313+
"nested": {"foo": "bar", "baz": "bing"},
314+
},
315+
"orders": {"schema": "main", "database": "jaffle_shop", "nested_list": ["b", "a", "c"]},
316+
}
317+
)
318+
319+
registry = JinjaMacroRegistry()
320+
registry.add_globals({"sources": {}, "refs": refs})
321+
322+
# Ensure that the AttributeDict string representation is sorted
323+
# in order to prevent an unexpected *visual* diff in ModelDiff
324+
# (note that the actual diff is based on the data hashes, so this is purely visual)
325+
expressions = registry.to_expressions()
326+
assert len(expressions) == 1
327+
assert (
328+
expressions[0].sql(dialect="duckdb")
329+
== "refs = {'orders': {'database': 'jaffle_shop', 'nested_list': ['a', 'b', 'c'], 'schema': 'main'}, 'payments': {'database': 'jaffle_shop', 'nested': {'baz': 'bing', 'foo': 'bar'}, 'schema': 'main'}}\n"
330+
"sources = {}"
331+
)

0 commit comments

Comments
 (0)