"""
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):
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)))
"""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 ' \
: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
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
# runtime information
self.undefined = undefined
+ self.cond_expr_undefined = cond_expr_undefined
self.optimized = optimized
self.finalize = finalize
self.autoescape = autoescape
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
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
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)