This is all part of the grammar of a language. In BASIC, when I first started to use it over 50 years ago, the assignment clause started with keyword LET
but this became optional, with the =
operator being both assignment and logic comparison.
Thus a = b = c
results in the variable a
being given the Boolean value of the test b = c
. Things get too complicated with a = b = c = d
.
The driver for what exactly happens, if anyone is interested, is the parsing process when the tokens (from lexical analysis) are converted into an Abstract Syntax Tree (AST). Whilst many languages have well defined syntax, Jinja2 appears to be less well defined, however there is access to the AST via the parse() function in the low-level API. After a bit of experimenting I managed to get this to work…
>>> import jinja2
>>> env = jinja2.Environment()
>>> env.parse("{% set v = 'a' %}{% set l = ['a','b','c'] %}{{ v in l}}")
Template(body=[
Assign(target=Name(name='v', ctx='store'), node=Const(value='a')),
Assign(target=Name(name='l', ctx='store'), node=List(items=[Const(value='a'), Const(value='b'), Const(value='c')])),
Output(nodes=[
Compare(expr=Name(name='v', ctx='load'), ops=[Operand(op='in', expr=Name(name='l', ctx='load'))])
])])
>>> env.parse("{% set v = 'a' %}{% set l = ['a','b','c'] %}{{ v is in l}}")
Template(body=[
Assign(target=Name(name='v', ctx='store'), node=Const(value='a')),
Assign(target=Name(name='l', ctx='store'), node=List(items=[Const(value='a'), Const(value='b'), Const(value='c')])),
Output(nodes=[
Test(node=Name(name='v', ctx='load'), name='in', args=[Name(name='l', ctx='load')], kwargs=[], dyn_args=None, dyn_kwargs=None)
])])
>>> env.parse("{% set v = 'a' %}{% set l = ['a','b','c'] %}{{ v in l|list}}")
Template(body=[
Assign(target=Name(name='v', ctx='store'), node=Const(value='a')),
Assign(target=Name(name='l', ctx='store'), node=List(items=[Const(value='a'), Const(value='b'), Const(value='c')])),
Output(nodes=[
Compare(expr=Name(name='v', ctx='load'), ops=[Operand(op='in', expr=Filter(node=
Name(name='l', ctx='load'), name='list', args=[], kwargs=[], dyn_args=None, dyn_kwargs=None))])
])])
>>> env.parse("{% set v = 'a' %}{% set l = ['a','b','c'] %}{{ v is in l|list}}")
Template(body=[
Assign(target=Name(name='v', ctx='store'), node=Const(value='a')),
Assign(target=Name(name='l', ctx='store'), node=List(items=[Const(value='a'), Const(value='b'), Const(value='c')])),
Output(nodes=[
Filter(node=
Test(node=Name(name='v', ctx='load'), name='in', args=[Name(name='l', ctx='load')], kwargs=[], dyn_args=None, dyn_kwargs=None), name='list', args=[], kwargs=[], dyn_args=None, dyn_kwargs=None)
])])
The key bits - the v in l
and v is in l
show, as expected, that
in
is parsed to the Compare node, using the value v
, operand in
and value l
is in
is parsed to the Test node, using the value v
, test name in
with argument value l
and therefore clearly in
is a comparison, and is in
is a test using ‘in’
Just to complete the exercise, I followed this up with adding the pipe list filter.
Again, as I surmised,
v in l|list
is parsed to Compare( v, op=in, arg= Filter( l, name=list ))
which shows that the list filter is applied first to the variable l
however, v is in l|list
is parsed to
Filter ( Test ( v, name=in, arg=l) name=filter)
which shows that the list filter is applied last, to the outcome of the ‘is in’ test.
All of which explains neatly why you get your error.
Cheers!