Block assignment with filter and autoescape
In the following code the result depends on atoescaping even though there are no characters which need escaping. Am I understanding atoescaping incorrectly?
If you set autoescape=False it works as expected.
There is a workaround (inspired by https://github.com/pallets/jinja/issues/486). I consider it a bug as it is exactly what the OP of https://github.com/pallets/jinja/issues/486 wanted to achieve from filtering.
The goal is to process (with a filter) whatever is inside the block assignment and store it to a variable. The filter is responsible for parsing the content and coercing it to a correct type (bool, number, string, lists). Note that the content of the block assignment can contain any Jinja instructions.
Expected Behavior
Prints POSTGRES
Actual Behavior
Prints ORACLE
Template Code
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("."), autoescape=True)
env.filters['parse_bool'] = lambda s: s == 'True'
# BUG
template = env.from_string("""
{% set ORACLE | parse_bool %}False{% endset %}
{{ 'ORACLE' if ORACLE else 'POSTGRES' }}
""")
print(template.render())
# WORKAROUND
template = env.from_string("""
{% set ORACLE %}False{% endset %}{% set ORACLE = ORACLE | parse_bool %}
{{ 'ORACLE' if ORACLE else 'POSTGRES' }}
""")
print(template.render())
Your Environment
- Python version: 3.8
- Jinja version: 2.11.2
I dug into what exactly is happening there, this is the code that jinja generates for the buggy template:
(I stripped some whitespace, my template looks like this: "{% set ORACLE | parse_bool %}False{% endset %}{{ 'ORACLE' if ORACLE else 'POSTGRES' }}")
from __future__ import generator_stop
from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, str_join, identity, TemplateNotFound, Namespace, Undefined
name = 'n'
def root(context, missing=missing, environment=environment):
resolve = context.resolve_or_missing
undefined = environment.undefined
cond_expr_undefined = Undefined
if 0: yield None
l_0_ORACLE = missing
t_1 = environment.filters['parse_bool']
pass
t_2 = []
pass
t_2.append(
'False',
)
l_0_ORACLE = (Markup if context.eval_ctx.autoescape else identity)(t_1(Markup(concat(t_2)))) # the issue happens here
context.vars['ORACLE'] = l_0_ORACLE
context.exported_vars.add('ORACLE')
yield escape(('ORACLE' if (undefined(name='ORACLE') if l_0_ORACLE is missing else l_0_ORACLE) else 'POSTGRES'))
blocks = {}
debug_info = '1=18'
The issue is the quite obvious - the "False" string is converted to markup, then to False by the filter, but then back to markup because autoescape is True. This points to that this is related to #490 . The markup calls are inserted by compiler here in visit_Filter and here in visit_AssignBlock
This needs to be solved by someone with deeper understanding of the Safe/Unsafe interactions in jinja, i.e. not me.