Skip to content

Commit

Permalink
make compile_templates deterministic
Browse files Browse the repository at this point in the history
Python3 doesn't keep insertion order for set(), so this sorts some
places for deterministic output for compiled template.
  • Loading branch information
atetubou committed May 27, 2021
1 parent d4e5112 commit 7598b9b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Version 3.0.2

Unreleased

- Make ``compile_templates`` deterministic. :issue:`1452, 1453`


Version 3.0.1
-------------
Expand Down
2 changes: 1 addition & 1 deletion src/jinja2/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
visitor.tests,
"tests",
):
for name in names:
for name in sorted(names):
if name not in id_map:
id_map[name] = self.temporary_identifier()

Expand Down
2 changes: 1 addition & 1 deletion src/jinja2/idtracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def dump_stores(self) -> t.Dict[str, str]:
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

Expand Down
76 changes: 76 additions & 0 deletions tests/test_compile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import os
import shutil
import tempfile

from jinja2 import Environment
from jinja2 import loaders


class TestCompileTemplate:
archive = None

def setup(self):
self.archive = tempfile.mkdtemp()

def teardown(self):
shutil.rmtree(self.archive)

def test_filters(self):
num_filter = 10

env = Environment(
loader=loaders.DictLoader(
{
"foo": "".join(
"{{ %d|filter%d }}" % (i, i) for i in range(num_filter)
)
}
)
)

for i in range(num_filter):
env.filters["filter%d" % i] = lambda value: value

env.compile_templates(self.archive, zip=None, ignore_errors=False)

files = os.listdir(self.archive)
assert len(files) == 1

with open(os.path.join(self.archive, files[0])) as f:
content = f.read()

for i in range(num_filter):
idx = content.find("environment.filters['filter%d']" % i)
assert idx >= 0
content = content[idx:]

def test_import_as_with_context(self):
num_import = 10

env = Environment(
loader=loaders.DictLoader(
{
"foo": "\n".join(
'{%% import "bar" as bar%d with context %%}' % i
for i in range(num_import)
)
}
)
)
env.compile_templates(self.archive, zip=None, ignore_errors=False)

files = os.listdir(self.archive)
assert len(files) == 1

with open(os.path.join(self.archive, files[0])) as f:
content = next(
filter(lambda line: "make_module" in line, f.read().splitlines())
)

for i in range(num_import):
print(i)
print(content)

idx = content.find("'bar%d': " % i)
assert idx >= 0
content = content[idx:]

0 comments on commit 7598b9b

Please sign in to comment.