black icon indicating copy to clipboard operation
black copied to clipboard

Black shouldn't merge multi-line string when last character on the line is '\n'

Open Conchylicultor opened this issue 5 years ago • 11 comments

Describe the style change

It may be related to https://github.com/psf/black/issues/1467, but it seems #1467 is fixed when trying on https://black.now.sh/ while this one still fail.

Currently, string split in multiple lines are merged together.

Input:

ValueError(
    'Invalid input:\n'
    f' * x={x}\n'
    f' * y={y}\n'
    f' * z={z}'
)

When the last character of the line is a \n, the code formatting match how the string will be displayed:

Invalid input:
 * x=12
 * y=41
 * z=0

Examples in the current Black style

Black merge all lines together, while keeping segments separated, which hurts readability:

ValueError("Invalid input:\n" f" * x={x}\n" f" * y={y}\n" f" * z={z}")

Desired style

Black shouldn't merge the lines together and keep the original formatting:

ValueError(
    'Invalid input:\n'
    f' * x={x}\n'
    f' * y={y}\n'
    f' * z={z}'
)

Conchylicultor avatar Jul 09 '20 00:07 Conchylicultor

Looks like a duplicate of #1467 (not sure, this should stay open for now). IMHO, I wouldn't exactly trust the online Black demo at https://black.now.sh, from my experience it seems unreliable for certain reproducing conditions.

ichard26 avatar Jul 20 '20 00:07 ichard26

@Conchylicultor With the introduction of #1132, the segments are now merged together.

bbugyi200 avatar Jul 22 '20 19:07 bbugyi200

@Conchylicultor With the introduction of #1132, the segments are now merged together.

But I don't want black to merge the string. Black should respect the \n, similarly to https://github.com/psf/black/issues/1467.

msg = (
    'Instructions:\n'
    ' 1) ...\n'
    ' 2) ...\n'
    ' 3) ...'
)

Is more readable than:

msg =  'Instructions:\n 1) ...\n 2) ...\n 3) ...'

As code formatting match the way the string is displayed.

Conchylicultor avatar Jul 22 '20 19:07 Conchylicultor

@Conchylicultor The case described by this issue is a bit different than #1467, since #1467 relates directly to #1132, whereas this issue does not.

I'm not saying that black shouldn't act the way you describe in this isue and the way described in #1467. I'm just pointing out that the code changes necessary in the two cases are very different.

bbugyi200 avatar Jul 22 '20 20:07 bbugyi200

@bbugyi200, thank you for the clarification. I understand now that #1132 wasn't trying to solve this issue.

Conchylicultor avatar Jul 22 '20 20:07 Conchylicultor

As a workaround you can put explicit + operators to prevent black from merging the string.

zsol avatar Sep 29 '21 07:09 zsol

using explicit "+" still results in the strings merging into 1 (using version=black, 21.12b0)

mvong-dlb avatar Dec 22 '21 02:12 mvong-dlb

My personal opinion is that this issue should be closed and #1467 should remain open.

Moreover, I think that this issue's example should continue to be formatted as it is currently (when the --experimental-string-processing flag is set, that is), since short strings should always be merged into one string when possible IMO. When string splitting / wrapping is necessary (i.e. for long strings), however, I agree that black should try to respect newlines in the string.

bbugyi200 avatar Dec 22 '21 16:12 bbugyi200

This issue is kind of popularizes use of "+" for string concatenation for me. 🥲

a = (
    "&SortByValue=priority&SortByOrder=asc"
    + "&IndexToStartPaging={3}"
    + "&NumberOfElementsToShow={2}"
    + "&CacheDurationMinutes=0"
    + "&AllowAlternativeResults=false"
)

Andrej730 avatar Mar 16 '23 06:03 Andrej730

Responding to a few previous points:

  • The issue as originally written makes sense to me and I'd welcome a PR adding this change to our preview style. We'll have to see how much impact this has on existing codebases though.
  • Regarding the discussion on whether this is a dupe of #1467, that issue is specific to the unstable string-processing feature, while this issue affects Black's current stable style, so I think they're best kept separate.
  • Re @Andrej730's comment: your example isn't affected by this issue since there are no \n in the string. However, maybe a case should be made that Black also shouldn't put adjacent strings on the same line even if they don't contain newlines.

JelleZijlstra avatar Jan 31 '25 18:01 JelleZijlstra

The simplest and most effective rule is to add line breaks after each string literal, regardless of the content (e.g. "\n"), if the number of concatenated string literals is ≥ 2.


Inputs:

f(
    "Data: "
    f"{foo: <4} | "
    f"{bar:8d} | "
    f"{qux: >16}"
)
f(        "Data: "     f"{foo: <4} | "
  f"{bar:8d} | "                f"{qux: >16}"      )
f(    "Data: " f"{foo: <4} | " f"{bar:8d} | " f"{qux: >16}"    )

Desired output 😀:

f(
    "Data: "
    f"{foo: <4} | "
    f"{bar:8d} | "
    f"{qux: >16}"
)

Current output ☹️:

f("Data: " f"{foo: <4} | " f"{bar:8d} | " f"{qux: >16}")

(Copied over from closed duplicate.)

YodaEmbedding avatar Sep 01 '25 00:09 YodaEmbedding