Skip to content

Commit 179df6b

Browse files
authored
Merge pull request #1190 from pallets/native-eval
native only evals at end of render
2 parents f1756a3 + f75cb42 commit 179df6b

File tree

3 files changed

+20
-24
lines changed

3 files changed

+20
-24
lines changed

CHANGES.rst

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Unreleased
2727
async environments. :issue:`1180`
2828
- Fix whitespace being removed before tags in the middle of lines when
2929
``lstrip_blocks`` is enabled. :issue:`1138`
30+
- :class:`~nativetypes.NativeEnvironment` doesn't evaluate
31+
intermediate strings during rendering. This prevents early
32+
evaluation which could change the value of an expression.
33+
:issue:`1186`
3034

3135

3236
Version 2.11.1

src/jinja2/nativetypes.py

+7-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import types
21
from ast import literal_eval
32
from itertools import chain
43
from itertools import islice
@@ -11,17 +10,14 @@
1110
from .environment import Template
1211

1312

14-
def native_concat(nodes, preserve_quotes=True):
13+
def native_concat(nodes):
1514
"""Return a native Python type from the list of compiled nodes. If
1615
the result is a single node, its value is returned. Otherwise, the
1716
nodes are concatenated as strings. If the result can be parsed with
1817
:func:`ast.literal_eval`, the parsed value is returned. Otherwise,
1918
the string is returned.
2019
2120
:param nodes: Iterable of nodes to concatenate.
22-
:param preserve_quotes: Whether to re-wrap literal strings with
23-
quotes, to preserve quotes around expressions for later parsing.
24-
Should be ``False`` in :meth:`NativeEnvironment.render`.
2521
"""
2622
head = list(islice(nodes, 2))
2723

@@ -31,37 +27,25 @@ def native_concat(nodes, preserve_quotes=True):
3127
if len(head) == 1:
3228
raw = head[0]
3329
else:
34-
if isinstance(nodes, types.GeneratorType):
35-
nodes = chain(head, nodes)
36-
raw = u"".join([text_type(v) for v in nodes])
30+
raw = u"".join([text_type(v) for v in chain(head, nodes)])
3731

3832
try:
39-
literal = literal_eval(raw)
33+
return literal_eval(raw)
4034
except (ValueError, SyntaxError, MemoryError):
4135
return raw
4236

43-
# If literal_eval returned a string, re-wrap with the original
44-
# quote character to avoid dropping quotes between expression nodes.
45-
# Without this, "'{{ a }}', '{{ b }}'" results in "a, b", but should
46-
# be ('a', 'b').
47-
if preserve_quotes and isinstance(literal, str):
48-
return "{quote}{}{quote}".format(literal, quote=raw[0])
49-
50-
return literal
51-
5237

5338
class NativeCodeGenerator(CodeGenerator):
5439
"""A code generator which renders Python types by not adding
55-
``to_string()`` around output nodes, and using :func:`native_concat`
56-
to convert complex strings back to Python types if possible.
40+
``to_string()`` around output nodes.
5741
"""
5842

5943
@staticmethod
6044
def _default_finalize(value):
6145
return value
6246

6347
def _output_const_repr(self, group):
64-
return repr(native_concat(group))
48+
return repr(u"".join([text_type(v) for v in group]))
6549

6650
def _output_child_to_const(self, node, frame, finalize):
6751
const = node.as_const(frame.eval_ctx)
@@ -100,10 +84,9 @@ def render(self, *args, **kwargs):
10084
Otherwise, the string is returned.
10185
"""
10286
vars = dict(*args, **kwargs)
87+
10388
try:
104-
return native_concat(
105-
self.root_render_func(self.new_context(vars)), preserve_quotes=False
106-
)
89+
return native_concat(self.root_render_func(self.new_context(vars)))
10790
except Exception:
10891
return self.environment.handle_exception()
10992

tests/test_nativetypes.py

+9
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ def test_concat_strings_with_quotes(env):
134134
assert result == "--host='localhost' --user \"Jinja\""
135135

136136

137+
def test_no_intermediate_eval(env):
138+
t = env.from_string("0.000{{ a }}")
139+
result = t.render(a=7)
140+
assert isinstance(result, float)
141+
# If intermediate eval happened, 0.000 would render 0.0, then 7
142+
# would be appended, resulting in 0.07.
143+
assert result < 0.007 # TODO use math.isclose in Python 3
144+
145+
137146
def test_spontaneous_env():
138147
t = NativeTemplate("{{ true }}")
139148
assert isinstance(t.environment, NativeEnvironment)

0 commit comments

Comments
 (0)