Skip to content
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

f-string evaluation of conditional expressions with != operator seems to fail #129093

Closed
JD-Veiga opened this issue Jan 20, 2025 · 11 comments
Closed
Labels
3.12 bugs and security fixes 3.13 bugs and security fixes 3.14 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-parser type-bug An unexpected behavior, bug, or error

Comments

@JD-Veiga
Copy link

JD-Veiga commented Jan 20, 2025

Bug report

Bug description:

Hi,

I really feel puzzled by this behaviour:

>>> print(f'{True == True=}')
True == True=True
>>> print(f'{True != True=}')
True False

I expected that the output of print(f'{True != True=}') would be True != True=False.

According to docs, "[t]o display both the expression text and its value after evaluation, (useful in debugging), an equal sign '=' may be added after the expression". So print(f'{True != True=}') fails because the expression text is not fully displayed -- even "=" is omitted.

Sorry if this is a duplicate -- I was not able to find any other report here and in Cython issue tracker.

Thank you.

CPython versions tested on:

3.12, 3.13

Operating systems tested on:

macOS

Linked PRs

@JD-Veiga JD-Veiga added the type-bug An unexpected behavior, bug, or error label Jan 20, 2025
@StanFromIreland
Copy link
Contributor

Tested on Windows 11 with 3.13 and same bug.

@picnixz picnixz added topic-parser 3.12 bugs and security fixes 3.13 bugs and security fixes 3.14 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Jan 20, 2025
@ericvsmith
Copy link
Member

Also:

>>> f'{1==0=}'
'1==0=False'
>>> f'{1!=0=}'
'1True'

@JD-Veiga
Copy link
Author

JD-Veiga commented Jan 20, 2025

Yeah! I think that any conditional expression containing != behaves in the same manner -- whatever the result of evaluating the expression is.

@ericvsmith
Copy link
Member

Yeah! I think that any conditional expression containing != behaves in the same manner -- whatever the result of evaluating the expression is.

Yes, I was just trying to point out that the True in the last evaluated expression comes not from the source code of the expression, but rather the value. Which is also why I removed the spaces around !=.

@JelleZijlstra
Copy link
Member

The AST looks like this:

>>> print(ast.dump(ast.parse("f'{1!=0=}'"), indent=True))
Module(
 body=[
  Expr(
   value=JoinedStr(
    values=[
     Constant(value='1'),
     FormattedValue(
      value=Compare(
       left=Constant(value=1),
       ops=[
        NotEq()],
       comparators=[
        Constant(value=0)]),
      conversion=114)]))])

So it does evaluate 1 != 0, but instead of outputting a constant 1!=0= (as it should) it outputs just 1. I suspect something is looking for the ! that introduces conversion specifiers and gets confused by the ! in the != operator.

A ! inside a string literal does work correctly though:

>>> f"{'a!b'=}"
"'a!b'='a!b'"

@sobolevn
Copy link
Member

sobolevn commented Jan 20, 2025

This is a parsing issue:

>>> ast.dump(ast.parse("f'{1 != 1=}'"))
"Module(body=[Expr(value=JoinedStr(values=[Constant(value='1 '), FormattedValue(value=Compare(left=Constant(value=1), ops=[NotEq()], comparators=[Constant(value=1)]), conversion=114)]))])"

>>> ast.dump(ast.parse("f'{1 == 1=}'"))
"Module(body=[Expr(value=JoinedStr(values=[Constant(value='1 == 1='), FormattedValue(value=Compare(left=Constant(value=1), ops=[Eq()], comparators=[Constant(value=1)]), conversion=114)]))])"

@tomasr8
Copy link
Member

tomasr8 commented Jan 20, 2025

This seems to fix it (with test_fstring passing), but I don't fully understand why yet:

diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c
index dbbb94a407c..3b433addbe1 100644
--- a/Parser/lexer/lexer.c
+++ b/Parser/lexer/lexer.c
@@ -212,7 +212,7 @@ _PyLexer_update_fstring_expr(struct tok_state *tok, char cur)
         case '}':
         case '!':
         case ':':
-            if (tok_mode->last_expr_end == -1) {
+            if (tok_mode->last_expr_end == -1 || cur == '}') {
                 tok_mode->last_expr_end = strlen(tok->start);
             }
             break;

I'll try to add some tests and perhaps open a PR tomorrow

@picnixz
Copy link
Member

picnixz commented Jan 21, 2025

Could this be related to #124363?

@JD-Veiga
Copy link
Author

JD-Veiga commented Jan 21, 2025

Could be, @picnixz, but I do not think so. As @JelleZijlstra pointed out:

I suspect something is looking for the ! that introduces conversion specifiers and gets confused by the ! in the != operator.

while #124363, as long as I understand, is related to raw strings and escape characters, which is not the case here.

The problem here seems to be that the expression text is printed up to ! while the expression itself is correctly evaluated, such as in the following example:

>> print(f'{(2 - 1) != 1=}')
(2 - 1) False

Notice that:

>> print(f'{(2 - 1) == 1=}')
(2 - 1) == 1=True

Even = sign is omitted in != expressions.

@pablogsal
Copy link
Member

Thanks everyone for the investigation and thanks @tomasr8 for the analysis. Fantastic job!

pablogsal added a commit that referenced this issue Jan 22, 2025
pablogsal pushed a commit that referenced this issue Jan 22, 2025
…hen expression contains `!` (GH-129159) (#129163)

gh-129093: Fix f-string debug text sometimes getting cut off when expression contains `!` (GH-129159)
(cherry picked from commit 767cf70)

Co-authored-by: Tomas R <tomas.roun8@gmail.com>
@JD-Veiga
Copy link
Author

Thanks to everybody. This was awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.12 bugs and security fixes 3.13 bugs and security fixes 3.14 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-parser type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

8 participants