black icon indicating copy to clipboard operation
black copied to clipboard

convert f-string with nothing to interpolate to regular string

Open raybuhr opened this issue 5 years ago • 8 comments

Is your feature request related to a problem? Please describe.

f-strings are great. sometimes they are so great we might use them even when we don't need to, such as in files where an f-string is used but nothing to interpolate was included in the string body.

Describe the solution you'd like

say you had a python script with a line like this:

hello = f"hello"

black should see that f-string, recognize that it could just be a regular string and drop the f-prefix.

output:

hello = "hello"

Describe alternatives you've considered

Could return an error message, but I want black to format for me. Could also not do this, but I think changing to a regular string is a better option.

raybuhr avatar Nov 12 '19 23:11 raybuhr

This can potentially be dangerous for f-strings with brackets.

s = f"this can be safely transformed to a regular string"
s2 = f"Here we have {{double_brackets}}, should they be replaced with single? Who knows..."

vemel avatar Nov 19 '19 04:11 vemel

As I see from the source code, f-string parsing is very poor for now.

Suggestion: what if we use ast.NodeVisitor for regular strings and f-strings and try to build a result from a tree.

  • no regexps needed to parse f-strings or choose quotes
  • quotes can be chosen from a list: ", ', """", ''''. if a new string uses qute from the list, we just pick the next available option. if we are out of quote options - abort formatting, Leaf stays as it is.
  • no need to implement full AST-to-string, visit_str, visit_bytes, visit_JoinedStr, visit_Num, visit_Name, visit_NameConstant should be enough. On unknown node - abort formatting, Leaf stays as it is.

I have some drafts that could help.

vemel avatar Nov 19 '19 05:11 vemel

#1132 sets up the ground work for this. Here is an example of how long f-strings should be expected to behave after this pull request is merged:

##### INPUT
fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one."

##### OUTPUT
fstring = (
    f"f-strings definitely make things more {difficult} than they need to be for"
    " {black}. But boy they sure are handy. The problem is that some lines will need"
    f" to have the 'f' whereas others do not. This {line}, for example, needs one."
)

As you can see, the f prefix is dropped for the middle string because it was not needed.

I had thought about implementing this in a more general form (as this issue requests). Converting f"hello" to "hello" modifies the AST, however, so I chose against it.

bbugyi200 avatar Mar 15 '20 05:03 bbugyi200

flake8 version 3.8.3 had error "F541 f-string is missing placeholders" that trips on exactly those kind of strings. While I understand the bright-line of "black doesn't change the AST", I have sort of gotten used to the crutch that "black will fix all the flake8 formatting problems" :) From an coder-in-the-editor perspective, removing the "f" from the middle string above feels about the same as removing it from a bare f"Hello".

reedstrm avatar Jul 27 '20 16:07 reedstrm

I'm OK with doing this; we already make known to be harmless AST changes in a few places. I believe on master we already remove useless fs in a few contexts.

JelleZijlstra avatar Jul 28 '20 00:07 JelleZijlstra

Wouldn't a linter be a better place for this type of transformation? Flake8 already flags this, but fails to offer an autofix - which IMO is why it's even considered for Black. If flake8 automatically fixed this for you, Black wouldn't need to.

zsol avatar May 28 '22 07:05 zsol

I feel like in general it's Black's job to make simple, safe formatting fixes.

JelleZijlstra avatar May 31 '22 01:05 JelleZijlstra

Half the time I forget to add the f prefix. The other half the time I forget to add the curly brackets around my var. I think automatically dropping a "looks useless to me" f prefix is a bad idea. How can black know what the user intended?

print(f"myvar=")
print("{myvar=}")

The same argument applies to the "automatically add f prefix.

Sorry, but :thumbsdown: for me.

dougthor42 avatar Jun 05 '22 18:06 dougthor42

Ruff can fix it automatically : https://beta.ruff.rs/docs/rules/f-string-missing-placeholders/

Nodd avatar Jun 02 '23 15:06 Nodd