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 d0c0299
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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
118 changes: 118 additions & 0 deletions tests/test_compile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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):
env = Environment(
loader=loaders.DictLoader(
{
"foo": """
{{ 1|filter1 }}
{{ 2|filter2 }}
"""
}
)
)

env.filters["filter1"] = lambda value: value
env.filters["filter2"] = 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:
assert f.read().splitlines() == [
"from __future__ import generator_stop",
"from jinja2.runtime import LoopContext, Macro, Markup,"
" Namespace, TemplateNotFound, TemplateReference,"
" TemplateRuntimeError, Undefined, concat, escape, identity,"
" internalcode, markup_join, missing, str_join",
"name = 'foo'",
"",
"def root(context, missing=missing):",
" resolve = context.resolve_or_missing",
" undefined = environment.undefined",
" cond_expr_undefined = Undefined",
" if 0: yield None",
" try:",
" t_1 = environment.filters['filter1']",
" except KeyError:",
" @internalcode",
" def t_1(*unused):",
' raise TemplateRuntimeError("No filter named'
" 'filter1' found.\")",
" try:",
" t_2 = environment.filters['filter2']",
" except KeyError:",
" @internalcode",
" def t_2(*unused):",
' raise TemplateRuntimeError("No filter named'
" 'filter2' found.\")",
" pass",
" yield '\\n1\\n2'",
"",
"blocks = {}",
"debug_info = ''",
]

def test_import_as_with_context(self):
env = Environment(
loader=loaders.DictLoader(
{
"foo": """
{% import "bar" as bar with context %}
{% import "baz" as baz with context %}
"""
}
)
)
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:
assert f.read().splitlines() == [
"from __future__ import generator_stop",
"from jinja2.runtime import LoopContext, Macro, Markup,"
" Namespace, TemplateNotFound, TemplateReference,"
" TemplateRuntimeError, Undefined, concat, escape, identity,"
" internalcode, markup_join, missing, str_join",
"name = 'foo'",
"",
"def root(context, missing=missing):",
" resolve = context.resolve_or_missing",
" undefined = environment.undefined",
" cond_expr_undefined = Undefined",
" if 0: yield None",
" l_0_bar = l_0_baz = missing",
" pass",
" yield '\\n'",
" l_0_bar = context.vars['bar'] = environment.get_template("
"'bar', 'foo').make_module(context.get_all(), True, {'bar':"
" l_0_bar, 'baz': l_0_baz})",
" context.exported_vars.discard('bar')",
" yield '\\n'",
" l_0_baz = context.vars['baz'] = environment.get_template("
"'baz', 'foo').make_module(context.get_all(), True, {'bar':"
" l_0_bar, 'baz': l_0_baz})",
" context.exported_vars.discard('baz')",
"",
"blocks = {}",
"debug_info = '2=13&3=16'",
]

0 comments on commit d0c0299

Please sign in to comment.