]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Use separate undefined object for CondExpr
authorAdrian Moennich <adrian@planetcoding.net>
Sat, 12 Oct 2019 14:18:34 +0000 (16:18 +0200)
committerAdrian Moennich <adrian@planetcoding.net>
Sun, 13 Oct 2019 14:32:02 +0000 (16:32 +0200)
CHANGES.rst
docs/templates.rst
jinja2/compiler.py
jinja2/environment.py
tests/test_api.py

index 305eb963a7f0460bf84c1b1ee17b4a6d0eb4eae1..1ddb8cabe0774c43142cd92deb9184ada79a6737 100644 (file)
@@ -39,6 +39,10 @@ Unreleased
     :issue:`63`
 -   When providing multiple paths to ``FileSystemLoader``, a template
     can have the same name as a directory. :issue:`821`
+-   Make the undefined object returned by ``{{ 'foo' if bar }}`` configurable,
+    and default to standard ``Undefined`` regardless of what is specified as
+    the environment's ``undefined`` object as doing so is not compatible with
+    ``StrictUndefined``. :issue:`710`, :pr:`1079`
 
 
 Version 2.10.3
index f6fb609b2e2baf12fa6192a3d15e79adc723993d..13b693b71ec2426799903c043af3bac6352403e9 100644 (file)
@@ -1355,7 +1355,8 @@ The general syntax is ``<do something> if <something is true> else <do
 something else>``.
 
 The `else` part is optional.  If not provided, the else block implicitly
-evaluates into an undefined object:
+evaluates into an undefined object (using the environment's undefined object
+specified in ``cond_expr_undefined``):
 
 .. sourcecode:: jinja
 
index 708186b1475b73aef7f66a7effe2a4e65e56495e..850b26c042fb3a6c027bd731add44cf0b678c2da 100644 (file)
@@ -609,6 +609,7 @@ class CodeGenerator(NodeVisitor):
         """
         self.writeline('resolve = context.resolve_or_missing')
         self.writeline('undefined = environment.undefined')
+        self.writeline('cond_expr_undefined = environment.cond_expr_undefined')
         self.writeline('if 0: yield None')
 
     def push_parameter_definitions(self, frame):
@@ -1623,7 +1624,7 @@ class CodeGenerator(NodeVisitor):
         def write_expr2():
             if node.expr2 is not None:
                 return self.visit(node.expr2, frame)
-            self.write('undefined(%r)' % ('the inline if-'
+            self.write('cond_expr_undefined(%r)' % ('the inline if-'
                        'expression on %s evaluated to false and '
                        'no else section was defined.' % self.position(node)))
 
index 2687889a3358eb7a9077eb35107d488d2b5f6f5d..477385c9f1b6ec031e2369afbde9fc3fd4f863f9 100644 (file)
@@ -101,6 +101,9 @@ def _environment_sanity_check(environment):
     """Perform a sanity check on the environment."""
     assert issubclass(environment.undefined, Undefined), 'undefined must ' \
         'be a subclass of undefined because filters depend on it.'
+    assert issubclass(environment.cond_expr_undefined, Undefined), \
+        'cond_expr_undefined must be a subclass of undefined because filters ' \
+        'depend on it.'
     assert environment.block_start_string != \
         environment.variable_start_string != \
         environment.comment_start_string, 'block, variable and comment ' \
@@ -183,6 +186,13 @@ class Environment(object):
             :class:`Undefined` or a subclass of it that is used to represent
             undefined values in the template.
 
+        `cond_expr_undefined`
+            :class:`Undefined` or a subclass of it that is used to represent
+            undefined values in the template for conditional expressions that
+            have no ``else`` part.  This is usually useful when using a more
+            strict undefined class without wanting to break the short version
+            of conditional expressions.
+
         `finalize`
             A callable that can be used to process the result of a variable
             expression before it is output.  For example one can convert
@@ -283,7 +293,8 @@ class Environment(object):
                  cache_size=400,
                  auto_reload=True,
                  bytecode_cache=None,
-                 enable_async=False):
+                 enable_async=False,
+                 cond_expr_undefined=Undefined):
         # !!Important notice!!
         #   The constructor accepts quite a few arguments that should be
         #   passed by keyword rather than position.  However it's important to
@@ -311,6 +322,7 @@ class Environment(object):
 
         # runtime information
         self.undefined = undefined
+        self.cond_expr_undefined = cond_expr_undefined
         self.optimized = optimized
         self.finalize = finalize
         self.autoescape = autoescape
@@ -363,7 +375,7 @@ class Environment(object):
                 extensions=missing, optimized=missing,
                 undefined=missing, finalize=missing, autoescape=missing,
                 loader=missing, cache_size=missing, auto_reload=missing,
-                bytecode_cache=missing):
+                bytecode_cache=missing, cond_expr_undefined=missing):
         """Create a new overlay environment that shares all the data with the
         current environment except for cache and the overridden attributes.
         Extensions cannot be removed for an overlayed environment.  An overlayed
@@ -936,14 +948,15 @@ class Template(object):
                 undefined=Undefined,
                 finalize=None,
                 autoescape=False,
-                enable_async=False):
+                enable_async=False,
+                cond_expr_undefined=Undefined):
         env = get_spontaneous_environment(
             block_start_string, block_end_string, variable_start_string,
             variable_end_string, comment_start_string, comment_end_string,
             line_statement_prefix, line_comment_prefix, trim_blocks,
             lstrip_blocks, newline_sequence, keep_trailing_newline,
             frozenset(extensions), optimized, undefined, finalize, autoescape,
-            None, 0, False, None, enable_async)
+            None, 0, False, None, enable_async, cond_expr_undefined)
         return env.from_string(source, template_class=cls)
 
     @classmethod
index a457602976a2a613b83392be12ef4f3d5729b43b..b29594b3766489e7ed41c79c54c4e351ade96a2f 100644 (file)
@@ -368,6 +368,15 @@ class TestUndefined(object):
         with pytest.raises(AttributeError):
             getattr(StrictUndefined, '__slots__')
 
+    def test_cond_expr_undefined(self):
+        env = Environment(cond_expr_undefined=StrictUndefined)
+        env2 = Environment(undefined=StrictUndefined)
+        pytest.raises(UndefinedError,
+                      env.from_string('{{ "foo" if false }}').render)
+        assert env.from_string('{{ ("foo" if false) is defined }}').render() == 'False'
+        assert env2.from_string('{{ "foo" if false }}').render() == ''
+        assert env2.from_string('{{ ("foo" if false) is defined }}').render() == 'False'
+
     def test_indexing_gives_undefined(self):
         t = Template("{{ var[42].foo }}")
         pytest.raises(UndefinedError, t.render, var=0)