black icon indicating copy to clipboard operation
black copied to clipboard

long blocks of unstructured text should be limited to 72 characters

Open fpdotmonkey opened this issue 3 years ago • 5 comments

Describe the style change

Per PEP8,

For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

[...]

Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the line length limit up to 99 characters, provided that comments and docstrings are still wrapped at 72 characters.

(emphasis mine)

So when the --experimental-string-processing flag is set, docstrings and very long normal strings should be wrapped at 72 characters.

Examples in the current Black style

def foo():
    """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
    Nonflowing().LongCode().that().has().many().structural().restrictions()
    Thingy("I am the very model of a modern Major-Gineral, I've information vegetable, animal, and mineral, I know the kings of England, and I quote the fights historical From Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical, I understand equations, both the simple and quadratical, About binomial theorem I'm teeming with a lot o' news, With many cheerful facts about the square of the hypotenuse.")

# output

def foo():
    """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
    Nonflowing().LongCode().that().has().many().structural().restrictions()
    Thingy(
        "I am the very model of a modern Major-Gineral, I've information"
        " vegetable, animal, and mineral, I know the kings of England, and I"
        " quote the fights historical From Marathon to Waterloo, in order"
        " categorical; I'm very well acquainted, too, with matters"
        " mathematical, I understand equations, both the simple and"
        " quadratical, About binomial theorem I'm teeming with a lot o' news,"
        " With many cheerful facts about the square of the hypotenuse."
    )

Desired style

def foo():
    """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
    Nonflowing().LongCode().that().has().many().structural().restrictions()
    Thingy("I am the very model of a modern Major-Gineral, I've information vegetable, animal, and mineral, I know the kings of England, and I quote the fights historical From Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical, I understand equations, both the simple and quadratical, About binomial theorem I'm teeming with a lot o' news, With many cheerful facts about the square of the hypotenuse.")

# output

def foo():
    """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
    eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
    ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
    aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    culpa qui officia deserunt mollit anim id est laborum.
    """
    Nonflowing().LongCode().that().has().many().structural().restrictions()
    Thingy(
        "I am the very model of a modern Major-Gineral, I've"
        " information vegetable, animal, and mineral, I know the kings"
        " of England, and I quote the fights historical From Marathon"
        " to Waterloo, in order categorical; I'm very well acquainted,"
        " too, with matters mathematical, I understand equations, both"
        " the simple and quadratical, About binomial theorem I'm"
        " teeming with a lot o' news, With many cheerful facts about"
        " the square of the hypotenuse."
    )

fpdotmonkey avatar May 31 '21 04:05 fpdotmonkey

I'm a bit ambivalent about this. First is the fact that we already increase PEP 8's line length from 79 to 88 by default. Then as an aside, if we did this I think it shouldn't apply to ordinary strings.

But let's have a look at what it would do. It's common to have docstrings indented at least two levels (class method), which including the quotations of a single-line docstring leaves 74 characters to write with an 88 line length, but only 58 with 72 chars. That's the difference between:

class Foo:
    def bar(self):
        """1234567890123456789012345678901234567890123456789012345678"""

    def baz(self):
        """12345678901234567890123456789012345678901234567890123456789012345678901234"""

which to me is significant especially when wanting to fit the docstring on one line. There is more room when the quotations are on separate lines though:

class Foo:
    def bar(self):
        """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
        incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
        nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
        fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
        culpa qui officia deserunt mollit anim id est laborum.
        """

    def baz(self):
        """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
        eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
        enim ad minim veniam, quis nostrud exercitation ullamco laboris
        nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
        reprehenderit in voluptate velit esse cillum dolore eu fugiat
        nulla pariatur. Excepteur sint occaecat cupidatat non proident,
        sunt inculpa qui officia deserunt mollit anim id est laborum.
        """

And I do appreciate the fact that it is easier on the eyes. So maybe there's something to be said about this! But let's be careful and consider the current processing and what adding this would mean. I for one can't see the whole picture yet.

felix-hilden avatar Jun 10 '21 11:06 felix-hilden

Another thought is that according to PEP 257,

The docstring of a script (a stand-alone program) should be usable as its "usage" message, printed when the script is invoked with incorrect or missing arguments (or perhaps with a "-h" option, for "help").

Some common argument parsers like argparse (notably part of the standard library) format their help messages to be 79 characters wide, so it would seem there's a conflict caused by low adherence to this rule.

Anyway, for long running copy I think it's a big win. Maybe less so for short or structured text.

fpdotmonkey avatar Jun 12 '21 09:06 fpdotmonkey

This is also related to #1713 which aims to introduce comment splitting.

felix-hilden avatar Jun 16 '21 13:06 felix-hilden

I'm not totally sure about the impact of this, but figured it may be worth pointing out that these are technically different strings:

foo = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""

bar = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
    eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
    ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
    aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    culpa qui officia deserunt mollit anim id est laborum.
    """

# foo != bar

As with Black's current approach for other strings, they would need to be wrapped in parenthesis to remain equivalent:

bar = ("""Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do """
    """eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim """
    """ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut """
    """aliquip ex ea commodo consequat. Duis aute irure dolor in """
    """reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla """
    """pariatur. Excepteur sint occaecat cupidatat non proident, sunt in """
    """culpa qui officia deserunt mollit anim id est laborum."""
    )

# foo == bar

I can't imagine any scenarios where this would affect docstrings specifically, but in other places using triple quotes to surround long blocks of text, we wouldn't want Black changing the literal representation of the strings by adding whitespace to the start of each line.

MrAlexBailey avatar Dec 10 '21 01:12 MrAlexBailey

Perhaps this issue should apply narrowly to docstrings since what gets rendered by sphinx et al is invariant with formatting

fpdotmonkey avatar Dec 23 '21 17:12 fpdotmonkey

Reflowing text in comments or docstrings is out of scope for Black. It is too difficult to do programmatically and consistently without producing ugly output.

We could apply a different line length limit for multi-line implicitly concatenated strings, but that's also problematic. We couldn't just use 72 everywhere, because line length is configurable. I don't want to add a new configuration option for this, or apply a heuristic like "normal line length - 16". Let's keep the current behavior of applying the same line length to all code.

JelleZijlstra avatar Apr 29 '23 02:04 JelleZijlstra