make compile_templates deterministic

Python3 doesn't keep insertion order for set(), so this sorts some
places for deterministic output for compiled template.
This commit is contained in:
Takuto Ikuta 2021-05-26 17:09:33 +09:00 committed by David Lord
parent 02071b3e59
commit 4c703ec44d
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
4 changed files with 32 additions and 2 deletions

View File

@ -7,6 +7,8 @@ Unreleased
- Fix a loop scoping bug that caused assignments in nested loops
to still be referenced outside of it. :issue:`1427`
- Make ``compile_templates`` deterministic for filter and import
names. :issue:`1452, 1453`
Version 3.0.1

View File

@ -556,7 +556,7 @@ class CodeGenerator(NodeVisitor):
visitor.tests,
"tests",
):
for name in names:
for name in sorted(names):
if name not in id_map:
id_map[name] = self.temporary_identifier()

View File

@ -149,7 +149,7 @@ class Symbols:
node: t.Optional["Symbols"] = self
while node is not None:
for name in node.stores:
for name in sorted(node.stores):
if name not in rv:
rv[name] = self.find_ref(name) # type: ignore

28
tests/test_compile.py Normal file
View File

@ -0,0 +1,28 @@
import os
import re
from jinja2.environment import Environment
from jinja2.loaders import DictLoader
def test_filters_deterministic(tmp_path):
src = "".join(f"{{{{ {i}|filter{i} }}}}" for i in range(10))
env = Environment(loader=DictLoader({"foo": src}))
env.filters.update(dict.fromkeys((f"filter{i}" for i in range(10)), lambda: None))
env.compile_templates(tmp_path, zip=None)
name = os.listdir(tmp_path)[0]
content = (tmp_path / name).read_text("utf8")
expect = [f"filters['filter{i}']" for i in range(10)]
found = re.findall(r"filters\['filter\d']", content)
assert found == expect
def test_import_as_with_context_deterministic(tmp_path):
src = "\n".join(f'{{% import "bar" as bar{i} with context %}}' 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"'bar{i}': " for i in range(10)]
found = re.findall(r"'bar\d': ", content)[:10]
assert found == expect