Make compiled output deterministic for tuple unpacking in set tag (#2022)
This commit is contained in:
commit
39d9ffff1f
@ -20,6 +20,8 @@ Unreleased
|
||||
async-aware filter. :issue:`1781`
|
||||
- ``|int`` filter handles ``OverflowError`` from scientific notation.
|
||||
:issue:`1921`
|
||||
- Make compiling deterministic for tuple unpacking in a ``{% set ... %}``
|
||||
call. :issue:`2021`
|
||||
|
||||
|
||||
Version 3.1.4
|
||||
|
||||
@ -811,7 +811,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.writeline("_block_vars.update({")
|
||||
else:
|
||||
self.writeline("context.vars.update({")
|
||||
for idx, name in enumerate(vars):
|
||||
for idx, name in enumerate(sorted(vars)):
|
||||
if idx:
|
||||
self.write(", ")
|
||||
ref = frame.symbols.ref(name)
|
||||
@ -821,7 +821,7 @@ class CodeGenerator(NodeVisitor):
|
||||
if len(public_names) == 1:
|
||||
self.writeline(f"context.exported_vars.add({public_names[0]!r})")
|
||||
else:
|
||||
names_str = ", ".join(map(repr, public_names))
|
||||
names_str = ", ".join(map(repr, sorted(public_names)))
|
||||
self.writeline(f"context.exported_vars.update(({names_str}))")
|
||||
|
||||
# -- Statement Visitors
|
||||
|
||||
@ -26,3 +26,64 @@ def test_import_as_with_context_deterministic(tmp_path):
|
||||
expect = [f"'bar{i}': " for i in range(10)]
|
||||
found = re.findall(r"'bar\d': ", content)[:10]
|
||||
assert found == expect
|
||||
|
||||
|
||||
def test_top_level_set_vars_unpacking_deterministic(tmp_path):
|
||||
src = "\n".join(f"{{% set a{i}, b{i}, c{i} = tuple_var{i} %}}" for i in range(10))
|
||||
env = Environment(loader=DictLoader({"foo": src}))
|
||||
env.compile_templates(tmp_path, zip=None)
|
||||
name = os.listdir(tmp_path)[0]
|
||||
content = (tmp_path / name).read_text("utf8")
|
||||
expect = [
|
||||
f"context.vars.update({{'a{i}': l_0_a{i}, 'b{i}': l_0_b{i}, 'c{i}': l_0_c{i}}})"
|
||||
for i in range(10)
|
||||
]
|
||||
found = re.findall(
|
||||
r"context\.vars\.update\(\{'a\d': l_0_a\d, 'b\d': l_0_b\d, 'c\d': l_0_c\d\}\)",
|
||||
content,
|
||||
)[:10]
|
||||
assert found == expect
|
||||
expect = [
|
||||
f"context.exported_vars.update(('a{i}', 'b{i}', 'c{i}'))" for i in range(10)
|
||||
]
|
||||
found = re.findall(
|
||||
r"context\.exported_vars\.update\(\('a\d', 'b\d', 'c\d'\)\)",
|
||||
content,
|
||||
)[:10]
|
||||
assert found == expect
|
||||
|
||||
|
||||
def test_loop_set_vars_unpacking_deterministic(tmp_path):
|
||||
src = "\n".join(f" {{% set a{i}, b{i}, c{i} = tuple_var{i} %}}" for i in range(10))
|
||||
src = f"{{% for i in seq %}}\n{src}\n{{% endfor %}}"
|
||||
env = Environment(loader=DictLoader({"foo": src}))
|
||||
env.compile_templates(tmp_path, zip=None)
|
||||
name = os.listdir(tmp_path)[0]
|
||||
content = (tmp_path / name).read_text("utf8")
|
||||
expect = [
|
||||
f"_loop_vars.update({{'a{i}': l_1_a{i}, 'b{i}': l_1_b{i}, 'c{i}': l_1_c{i}}})"
|
||||
for i in range(10)
|
||||
]
|
||||
found = re.findall(
|
||||
r"_loop_vars\.update\(\{'a\d': l_1_a\d, 'b\d': l_1_b\d, 'c\d': l_1_c\d\}\)",
|
||||
content,
|
||||
)[:10]
|
||||
assert found == expect
|
||||
|
||||
|
||||
def test_block_set_vars_unpacking_deterministic(tmp_path):
|
||||
src = "\n".join(f" {{% set a{i}, b{i}, c{i} = tuple_var{i} %}}" for i in range(10))
|
||||
src = f"{{% block test %}}\n{src}\n{{% endblock test %}}"
|
||||
env = Environment(loader=DictLoader({"foo": src}))
|
||||
env.compile_templates(tmp_path, zip=None)
|
||||
name = os.listdir(tmp_path)[0]
|
||||
content = (tmp_path / name).read_text("utf8")
|
||||
expect = [
|
||||
f"_block_vars.update({{'a{i}': l_0_a{i}, 'b{i}': l_0_b{i}, 'c{i}': l_0_c{i}}})"
|
||||
for i in range(10)
|
||||
]
|
||||
found = re.findall(
|
||||
r"_block_vars\.update\(\{'a\d': l_0_a\d, 'b\d': l_0_b\d, 'c\d': l_0_c\d\}\)",
|
||||
content,
|
||||
)[:10]
|
||||
assert found == expect
|
||||
|
||||
Loading…
Reference in New Issue
Block a user