summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2020-04-13 08:25:49 -0700
committerDavid Lord <davidism@gmail.com>2020-04-13 08:51:38 -0700
commitf75cb42e4f272bf9ecc47e09fb46e27dfdfd24fb (patch)
tree571a88039c48dacc55517fbd5aea1c9771cc9418
parentf1756a3b44a6fd791954bd8d610cd9211d682518 (diff)
downloadjinja2-f75cb42e4f272bf9ecc47e09fb46e27dfdfd24fb.tar.gz
native only evals at end of render
Co-authored-by: Martin Krizek <mkrizek@redhat.com>
-rw-r--r--CHANGES.rst4
-rw-r--r--src/jinja2/nativetypes.py31
-rw-r--r--tests/test_nativetypes.py9
3 files changed, 20 insertions, 24 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index e6608f4..495f693 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -27,6 +27,10 @@ Unreleased
async environments. :issue:`1180`
- Fix whitespace being removed before tags in the middle of lines when
``lstrip_blocks`` is enabled. :issue:`1138`
+- :class:`~nativetypes.NativeEnvironment` doesn't evaluate
+ intermediate strings during rendering. This prevents early
+ evaluation which could change the value of an expression.
+ :issue:`1186`
Version 2.11.1
diff --git a/src/jinja2/nativetypes.py b/src/jinja2/nativetypes.py
index 9866c96..a9ead4e 100644
--- a/src/jinja2/nativetypes.py
+++ b/src/jinja2/nativetypes.py
@@ -1,4 +1,3 @@
-import types
from ast import literal_eval
from itertools import chain
from itertools import islice
@@ -11,7 +10,7 @@ from .environment import Environment
from .environment import Template
-def native_concat(nodes, preserve_quotes=True):
+def native_concat(nodes):
"""Return a native Python type from the list of compiled nodes. If
the result is a single node, its value is returned. Otherwise, the
nodes are concatenated as strings. If the result can be parsed with
@@ -19,9 +18,6 @@ def native_concat(nodes, preserve_quotes=True):
the string is returned.
:param nodes: Iterable of nodes to concatenate.
- :param preserve_quotes: Whether to re-wrap literal strings with
- quotes, to preserve quotes around expressions for later parsing.
- Should be ``False`` in :meth:`NativeEnvironment.render`.
"""
head = list(islice(nodes, 2))
@@ -31,29 +27,17 @@ def native_concat(nodes, preserve_quotes=True):
if len(head) == 1:
raw = head[0]
else:
- if isinstance(nodes, types.GeneratorType):
- nodes = chain(head, nodes)
- raw = u"".join([text_type(v) for v in nodes])
+ raw = u"".join([text_type(v) for v in chain(head, nodes)])
try:
- literal = literal_eval(raw)
+ return literal_eval(raw)
except (ValueError, SyntaxError, MemoryError):
return raw
- # If literal_eval returned a string, re-wrap with the original
- # quote character to avoid dropping quotes between expression nodes.
- # Without this, "'{{ a }}', '{{ b }}'" results in "a, b", but should
- # be ('a', 'b').
- if preserve_quotes and isinstance(literal, str):
- return "{quote}{}{quote}".format(literal, quote=raw[0])
-
- return literal
-
class NativeCodeGenerator(CodeGenerator):
"""A code generator which renders Python types by not adding
- ``to_string()`` around output nodes, and using :func:`native_concat`
- to convert complex strings back to Python types if possible.
+ ``to_string()`` around output nodes.
"""
@staticmethod
@@ -61,7 +45,7 @@ class NativeCodeGenerator(CodeGenerator):
return value
def _output_const_repr(self, group):
- return repr(native_concat(group))
+ return repr(u"".join([text_type(v) for v in group]))
def _output_child_to_const(self, node, frame, finalize):
const = node.as_const(frame.eval_ctx)
@@ -100,10 +84,9 @@ class NativeTemplate(Template):
Otherwise, the string is returned.
"""
vars = dict(*args, **kwargs)
+
try:
- return native_concat(
- self.root_render_func(self.new_context(vars)), preserve_quotes=False
- )
+ return native_concat(self.root_render_func(self.new_context(vars)))
except Exception:
return self.environment.handle_exception()
diff --git a/tests/test_nativetypes.py b/tests/test_nativetypes.py
index 77d378d..76871ab 100644
--- a/tests/test_nativetypes.py
+++ b/tests/test_nativetypes.py
@@ -134,6 +134,15 @@ def test_concat_strings_with_quotes(env):
assert result == "--host='localhost' --user \"Jinja\""
+def test_no_intermediate_eval(env):
+ t = env.from_string("0.000{{ a }}")
+ result = t.render(a=7)
+ assert isinstance(result, float)
+ # If intermediate eval happened, 0.000 would render 0.0, then 7
+ # would be appended, resulting in 0.07.
+ assert result < 0.007 # TODO use math.isclose in Python 3
+
+
def test_spontaneous_env():
t = NativeTemplate("{{ true }}")
assert isinstance(t.environment, NativeEnvironment)