PyCQA / pycodestyle

Simple Python style checker in one Python file
https://pycodestyle.pycqa.org
Other
5.01k stars 755 forks source link

False positive E202 and E251 inside f-string (Python 3.12) #1201

Closed AndrewUshakov closed 10 months ago

AndrewUshakov commented 10 months ago

Hi!

For the code below:

a = 10
print(f'{a = }')

the pycodestyle.py version 2.11.0 generates 3 false positive errors:

tst.py:2:11: E251 unexpected spaces around keyword / parameter equals
tst.py:2:13: E202 whitespace before '}'
tst.py:2:13: E251 unexpected spaces around keyword / parameter equals

P.S. Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct 2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)] on win32

Thank you in advance.

Andrew

asottile commented 10 months ago

I believe the intended syntax for this is f'{a=}' which does not produce errors

AndrewUshakov commented 10 months ago

Hi!

In the proposal https://github.com/python/cpython/issues/80998, where discussed syntax was suggested, you can find example of the f-string with spaces around the = symbol.

In the section https://docs.python.org/3/reference/lexical_analysis.html#f-strings you can also find example of the f-string with more spaces than in my example:

>f"{ foo = }" # preserves whitespace

One of the new features In the Python 3.12 is: "More flexible f-string parsing , allowing many things previously disallowed (PEP 701)." In the PEP701 documentation the special section "Handling of f-string debug expressions" exists. The fragment of this section is below:

f-string debug expressions preserve whitespace in the expression, including spaces after the { and the = characters. This means that the raw string contents of the expression part of the f-string must be kept intact and not just the associated tokens.

As I understand, it means that:

f'{ foo = }'

is equivalent to

f' foo = {foo}'

and only expression itself without surrounding spaces need to be validated against PEP8.

Additionally, in the PEP701 documentation you can find examples of "usual" f-strings with spaces after { and before }. Your script generates E201 and E202 for such strings, while real warnings should look like "spaces after { and before } will disappear at the output". And, of course, E251 message is confusing, because meaning of = character is different here.

Best regards, Andrew

asottile commented 10 months ago

yes but all of the canonical examples do not have spaces

AndrewUshakov commented 10 months ago

By the way, I found one case (agree, very special) where the space after the { is required. Consider the valid Python 3.12 code below, which uses "usual" f-string:

a = 999
print(f'{ {a, 2, 3}}')
print(f'{{a, 2, 3}}')

which prints:

{2, 3, 999}
{a, 2, 3}

Process finished with exit code 0

Do you agree, that it is not PEP8 error/warning?

asottile commented 10 months ago

use parentheses

AndrewUshakov commented 10 months ago

Result of

print(f'{({a, 2, 3})=}')

looks ugly:

({a, 2, 3})={2, 3, 999}
asottile commented 10 months ago

it looks ugly either way. nobody would write code like that

AndrewUshakov commented 10 months ago

Ok. The last question. Will you accept pull request?

P.S. Yes, I understand, that patch will not be trivial, because in the function extraneous_whitespace tokens are unavailable.

asottile commented 10 months ago

no. I consider the current behavior to be correct. you can use noqa for your strawman cases

yous commented 10 months ago

The spaces around = in f-strings change the output.

foo = 42
print(f"{foo=}")
# foo=42
print(f"{foo =}")
# foo =42
print(f"{foo= }")
# foo= 42
print(f"(foo = }")
# foo = 42

What will be the effective way to produce foo = 42 without writing the variable name twice in the code? (i.e. f"foo = {foo}")

Generally E251 errors won't affect the behavior of the code, but this one forces to change the behavior.