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

Black doesn't format expressions inside f-strings #567

Open
mxr opened this issue Oct 16, 2018 · 15 comments
Open

Black doesn't format expressions inside f-strings #567

mxr opened this issue Oct 16, 2018 · 15 comments
Labels
C: parser How we parse code. Or fail to parse it. F: strings Related to our handling of strings T: enhancement New feature or request T: style What do we want Blackened code to look like?

Comments

@mxr
Copy link

mxr commented Oct 16, 2018

Operating system: macOS 10.13.6
Python version: 3.7.0
Black version: 18.9b0
Does also happen on master: yes


Given this example

prefix = "foo"
print(f"{ prefix        }.bar")

I was expecting black to trim the whitespace inside the f-string braces, but black leaves the file as-is.

Is this a bug or expected?

@zsol
Copy link
Collaborator

zsol commented Oct 16, 2018

This is an unfortunate side effect of how the AST encodes f-strings :( It would be awesome if black would understand expressions inside f-strings, but that's not the case today.

@zsol zsol added the T: enhancement New feature or request label Oct 16, 2018
@ambv
Copy link
Collaborator

ambv commented May 7, 2019

This is not handled currently because:

  • lib2to3 handles f-strings like regular strings; and
  • users probably expect a different set of formatting rules within the braces.

In particular, we cannot use newlines inside the braces and we probably should minimize the amount of whitespace compared to regular code.

@janosh
Copy link

janosh commented Jan 24, 2020

The title makes this issue a little hard to find. Something like "black not formatting expressions inside f-strings" would make it easier.

@zsol zsol changed the title Leading/trailing whitespace in f-string Black doesn't format expressions inside f-strings Jan 24, 2020
@ambv
Copy link
Collaborator

ambv commented Mar 4, 2020

This is a design problem. Should we enforce the same style for expressions in f-strings as we do outside of them? In particular, spaces around operators come to mind.

@vpoulailleau
Copy link

I came here to write the same kind of issue 😉 BTW, thanks so much for black, this is a great tool!

To my mind, yes the formatting should be the same inside and outside an f-string. But at least, PEP 8 should be respected: "Use your own judgment; however, never use more than one space, and always have the same amount of whitespace on both sides of a binary operator"

Especially when applied to:

x = f"{3  +      5}"

@ericvsmith
Copy link

Two points:

  1. I've tried on multiple occasions to make CPython use the grammar to parse f-strings, but it's complicated. Maybe the new PEG parser will help here, but I'm not optimistic. I really should write up a description of the problem someday. Basically it's a lexer problem, not a parser problem, but I'm not sure where the PEG parser draws the line. Now that PEP 617 has been accepted I'll dig into it deeper.

  2. With the "f-string debugging" feature (a name I do not love), whitespace becomes significant between the expression and before and after the equal sign, so you'd need to be aware of that. Hopefully if the first point is addressed you might get some help here.

>>> f'{x=}'
"x='foo'"
>>> f'{x = }'
"x = 'foo'"

I think the significant whitespace here is perhaps too clever, but it solved a design problem.

@JelleZijlstra JelleZijlstra added C: parser How we parse code. Or fail to parse it. F: strings Related to our handling of strings labels May 30, 2021
@JelleZijlstra
Copy link
Collaborator

We should really fix this at some point. Here are some thoughts in no particular order:

  • Fix determination of f-string expression spans #2654 gives us a way to find the f-string bits in a string. That could be reused to then format those bits. We'd need to somehow communicate to the string mangler what grammar we're using, then call recursively into format_str.
  • But it's not that simple. Reformatting the f-string expression may change its length, which can in turn influence how we split the line. Also, we need to be careful with quotes because if the outer string uses ", the inner one can't. (Format nested string quotes #2827 has some nice examples here. I think we should default to double quotes on the outer string, then default the inner string's quotes to single.)
  • Thankfully the grammar doesn't allow putting newlines inside f-string expressions, so we don't have to consider how to handle those.
  • @ambv asked above whether we should use different formatting rules within f-strings. I am going to make a tentative call that we should not (except where the syntax requires changes, as with string quoting): our style should be consistent. But if there's specific cases where it makes sense to do things differently within f-strings, I'm happy to change my mind.
  • @ericvsmith brings up 3.8's debug expressions (https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging). Those add the additional wrinkle that formatting the f-expression will change the AST, because the exact text of the expression is part of the AST. We'll have to either relax the AST safety check in this context or skip reformatting on f-expressions with the debug specifier.

@zsol
Copy link
Collaborator

zsol commented Jan 29, 2022

For what it's worth, I believe this would be close to trivial to implement on top of LibCST, which now parses f-strings inside the grammar, and so it provides a simple API to expose the parts of the f-string expression: https://libcst.readthedocs.io/en/latest/nodes.html#formatted-strings-f-strings

@robwijnhoven
Copy link

robwijnhoven commented May 4, 2023

We have experienced an issue with black when splitting f-strings over multiple lines.
Probably this is a similar to the issue mentioned above, but adding it here for completeness.

string = f"{variable1} some text here so that this line becomes way too long for my line-limit {variable2}."

Black now formats this into (ignoring spacing):

string = f"{variable1} some text here so that this line becomes way "
+ "too long for my line-limit {variable2}."

while it should be adding the "f-" to the second split line:

string = f"{variable1} some text here so that this line becomes way "
+ f"too long for my line-limit {variable2}."

@JelleZijlstra
Copy link
Collaborator

@robwijnhoven I don't believe that's how Black formatting has ever worked. If you can find a reproducible example, please report a separate bug.

@lts20050703
Copy link

Was having basic python classes today, tried to use ternary if else in f string,
print(f"{a} / {b} = {"Cannot divide by 0" if b == 0 else f"{a/b:.2f}"}")
ended up with the following error
error: cannot format C:\Users\lts20\Git\py\2023.11.08\Exer_1.py: Cannot parse: 16:22: print(f"{a} / {b} = {"Cannot divide by 0" if b == 0 else f"{a/b:.2f}"}")
Is this intended?

@hauntsaninja
Copy link
Collaborator

That is only valid syntax on Python 3.12 or newer, support for that is tracked at #3746

@JelleZijlstra
Copy link
Collaborator

Once #3822 is merged it should be much more tractable to fix this long-standing issue.

@JohnVillalovos
Copy link

Once #3822 is merged it should be much more tractable to fix this long-standing issue.

It appears that #3822 has been merged. Any updates on possibly moving forward on this issue?

@JelleZijlstra
Copy link
Collaborator

We'll need another contributor to step up and propose a PR to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: parser How we parse code. Or fail to parse it. F: strings Related to our handling of strings T: enhancement New feature or request T: style What do we want Blackened code to look like?
Projects
None yet
Development

No branches or pull requests