-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Unexpected TypeError when using boolean test inside numeric expression #755
Comments
I've tried tracking this down myself, but I'm having difficulty figuring out how to get a dump of the python source generated by the jinja compilation process (if that's in fact where the problem lies). I'd definitely appreciate any pointers. |
So, I agree with the assessment made here, but if you use the "sameas" function this all works exactly as expected. Examples:
Likewise, both "is eq" and "is equalto" work as expected. It's just the double equals that returns the wrong value. This doesn't fix this ticket, but if anyone is stuck, a good workaround is to use any of the comparison functions except "==". |
I think this might be the cause of the strange behavior I've found here. from jinja2 import Environment
expr = Environment().compile_expression
ctx = dict(foo="baz", bar="")
# Recreating `bool(string)` behaves as you expect.
expr("(foo | length) != 0")(ctx) # True
expr("(bar | length) != 0")(ctx) # False
# Comparing them, however...
expr("((foo | length) != 0) != ((bar | length) != 0)")(ctx) # False
# Huh? Do I not remember how boolean logic works?
expr("foo != bar")(foo=True, bar=False) # True
# Let's try another syntax, which should be equivalent:
expr("((foo | length) != 0) is not eq((bar | length) != 0)")(ctx) # True
# Strange. Let's also try the ruby/js style of casting to a boolean:
expr("(not not foo) != (not not bar)")(ctx) # True
# And just out of curiosity, what if we applied the non-operator way to the length tests themselves?
expr("((foo | length) is not eq(0)) != ((bar | length) is not eq(0))")(ctx) # True
|
I did a bit of investigation. It appears that somewhere in the process of converting a statement containing one or more boolean comparisons between a variable and another object into the underlying bytecode, it drops the parens around the comparisons, but it keeps them for entirely numeric operations. For example, it considers the first two equations below identical, but considers the third different (which at least is correct):
It works correctly if the comparison is between two predefined constants. Here's an incredibly simple test that should probably be added to the unit tests somewhere:
(I can do this later, but I have to get back to my actual job so I can't do that now.) If the expression is too complicated to just break out to not use parentheses, you can get around this by breaking the boolean part into a separate variable (set temp=boolean result, then use temp in the larger equation). Below is the script I used to determine this, if anyone who knows more about the parsing stack wants to dig further. (I'll try to look into it more later but I've never touched the internals of Jinja2 so I'll probably get bogged down before finding anything.)
|
Did some more debugging, ending up with the following script showing that it lies in the compilation of the parser syntax tree into the underlying code to be executed. Checked the code, looks like the compiler isn't adding parens when it generates that code in visit_Compare. Created pull request #938 with the fix. (first blob is script, second is its output)
|
Fixed by #938 |
I got an unexpected TypeError when rendering the template string:
{% set x = None %}{{ 2 * (x == "foo")}}
.My best guess is that something in this fragment is being incorrectly compiled to python code.
Full test case:
Expected behavior is that render would return
"0"
.Some other similar template strings:
{% set x = "bar" %}{{ 2 * (x == "foo") }}
unexpectedly renders"False"
, rather than"0"
.{% set x = "foo" %}{{ 2 * (x == "foo") }}
unexpectedly renders"False"
, rather than"2"
.{% set x = "foo" %}{{ (x == "foo") }}
correctly renders"True"
.{{ 2 * True }}
correctly renders"2"
.Tested this with jinja 2.0 - 2.9.6, under python 2.7 & 3.5.
The text was updated successfully, but these errors were encountered: