PEP-8: clarify if multiline argument list with a closing `)` on a separate line is acceptable

As the issue has raised in several python projects (including pylint and black) I think PEP-8 would benefit from being a bit more precise

PEP 8 – Style Guide for Python Code | peps.python.org states

Continuation lines should align wrapped elements either vertically using Python’s implicit line joining inside parentheses, brackets and braces, or using a hanging indent [7]. When using a hanging indent the following should be considered; there should be no arguments on the first line and further indentation should be used to clearly distinguish itself as a continuation line.

and then gives two examples

in yes it gives :

foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

(with 1 indentation)

and in no it gives

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

my question:

is

def long_function_name(
    var_one, var_two, var_three,
    var_four
):  # <----   it visually make clear that print is not inside the argument list , so it is now distinguishable
    print(var_one)

“possible” ?

especially if you see this other example

    # Add a comment, which will provide some distinction in editors
    # supporting syntax highlighting.
    if (this_is_one_thing and
        that_is_another_thing):
        # Since both conditions are true, we can frobnicate.
        do_something()

where a comment is given as possible way to have only 1 indentation

so at the very least the docstring should play the same role and allows :

def long_function_name(
    var_one, var_two, var_three,
    var_four
): 
    """The doc string is also a comment """ 
    print(var_one)
1 Like

Personally, I use that style in some cases, when it’s more readable (to me, at least). I would be sad if style checker tools for some reason decided that rejecting such a style was OK.

Having said that, I don’t really care what the style is for the Python standard library itself (which is what PEP 8 is actually about, regardless of the fact that tools seem to think that it’s the “one true style” for all Python code) and I definitely don’t feel that it’s something where PEP 8 should pronounce one style as preferable over the other. Both have their places, and the programmer’s judgement should apply here (just as anywhere else, but again that seems to be a point that gets lost in “let the style checker set the rules” policies…)

3 Likes

exactly my feeling, I’m sorry if I haven’t expressed myself correctly, my goal was to make it clearer that both are correct i.e (and that pep-8 goal is readability more than one the defining the one true style) :slight_smile:

I think for this, just adding one more example in pep-8 examples maybe good enough ?

In such cases I prefer to use two indentation levels.

def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)
2 Likes

yes here it’s more about tackling the making it clearer that pep-8 allows both, rather than “which one do you prefer” (for which I understand that both side have rational arguments to prefer a given one), so I would prefer the discussion to focus more on "is the style in the first post an acceptable one per pep-8 ‘spirit’ , if so, can we add an example of such style in pep-8 in the acceptable examples, if not can we add it explicitly in the ‘no’ with the reason why it’s against pep-8 spirit.

Remember that PEP 8 is the style guide for Python itself, and while the community often times chooses to use it as their own style guide, it isn’t meant to cover all cases. So in this instance I would say it covers what it’s supported to with the one style we prefer in Python itself. Now that doesn’t say other styles aren’t equally as valid, it’s just that we don’t choose to use it.

3 Likes

So the root of this argument is that Black produces code that isn’t always in compliance with PEP 8. I say this is fine – if you use Black you don’t need PEP 8 to tell you how to format your code. (PEP 8 is still useful for other things like naming things.)

Black optimizes for a different goal than PEP 8; PEP 8 optimizes for readability while Black sometimes compromises readability in order to satisfy other goals such as minimizing lines changed as code evolves.

I don’t want to compromise PEP 8 though.

4 Likes

Thanks for your answer, yes the goal is not about saying that this given style should be how the stdlib should formatted (as anyway pep-8 permits several style).

Actually it was more about I’ve seen nothing in PEP-8 stating it was not compliant. Can you guide me to the line that makes it “invalid per pep-8” ? it’s not only about black, there’s other tool like pylint etc. where people were arguing about

def foo(
   bar,
   ter
):
   """my docstring"""
   print("something")

being not acceptable as pep-8.

However as pep-8 state

or it may be lined up under the first character of the line that starts the multiline construct, as in:

my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
‘a’, ‘b’, ‘c’,
‘d’, ‘e’, ‘f’,
)

having ): on a separate line valid seems covered by it right ? and that this visual separation + docstring is enough to avoid the ambiguity (as per the if example above), makes it difficult to not seeing as being in the limit of pep-8 consider acceptable.

I would interpret the examples you quote as supporting the use of ): on a separate line, should the programmer wish to use that style. However, I would also be happy if someone said that PEP 8 takes no stance on this style.

Surely if someone wants to prohibit a particular style, then:

  1. They are entirely within their rights to do so in their own projects, regardless of what PEP 8 says.
  2. They have no authority to dictate what other projects choose to do, again regardless of PEP 8.

If someone wants to assert that PEP 8 prohibits this style, then (a) the burden is upon them to demonstrate where PEP 8 makes that assertion, and (b) it seems to me like a pointless waste of their time to make that assertion, given the 2 points above.

The only borderline case is tools like pycodestyle, that do style checking for projects, which may need to interpret PEP 8 fairly strictly to implement a “PEP 8 conformant” style. But debates about how they choose to interpret PEP 8 are really a matter for them - if their users disagree with their interpretation, they can add flags to allow the users to modify the base style, document their interpretation, or whatever they choose.

3 Likes

I would prefer PEP 8 to explicitly discourage putting ): on a line by itself, since I find it ugly, Black notwithstanding. This is different from putting the last bracket of a tuple, list or call on a line by itself, since there’s nothing following that’s indented relative to the start of the construct.

5 Likes

The trick with style guides is the potential list of unacceptable formats is nearly infinite while documenting what is acceptable is tractable. So assume if something is not listed as acceptable then it isn’t :wink:.

1 Like

haha, for that specific quote I was more talking about definitions rather than examples , for which I agree with you , you can’t list them all (maybe provide the generator function :slight_smile: )

thanks to all of you for the clear and straight to the point discussion. I guess I got the clarification I wanted, even though it didn’t go the way I would have preferred :slight_smile: the key learning I got from there

  1. pep-8 has always been for “internal use” and is not meant to be enforced outside, that some people follow it outside and some preach it as the “one true way” is an unintended consequence.
  2. ): though at first ambiguous from my reading, was actually not in the mind of those who written pep-8, therefore should not be considered as pep-8 compliant.
  3. some of the prominent members of python community sees no issue with black enforcing a different code-style as long as it’s outside of python official projects.

thanks again for taking the time to answering me.

1 Like

Sorry to jump in when the thread is a few days old, but I don’t see that anyone else has mentioned these points:

As I recall, every example in PEP 8 that has the closing delimiter on the following line also has a trailing comma after the last element, and vice versa - indeed, “no trailing comma on the same line except for single-element tuples” is an explicitly stated rule. I had taken the reverse (comma required if the delimiter is on its own line) to therefore also be an implicit rule. Maybe it would be worth discussing explicitly?

Also, no-one has mentioned return annotations. Placing a line break before the closing parenthesis is the only way to place the return annotation on a line by itself without resorting to a backslash line continuation. Placing the line break between ) -> or after -> results in a syntax error.

1 Like

I would prefer PEP 8 to explicitly discourage putting ): on a line by itself, since I find it ugly, Black notwithstanding. This is different from putting the last bracket of a tuple, list or call on a line by itself, since there’s nothing following that’s indented relative to the start of the construct.

The rationale behind this exception (the closing brace of a multiline construct may be on a line by itself except for ):) makes perfect sense and I also concur with @guido that PEP 8 should be explicit about it. Like the OP @allan-simon I had the same question about this PEP 8 ambiguity. And I suspect that Black incorrectly chose to follow the general rule (the closing brace of a multiline construct may be on a line by itself) in all cases without considering the exception (except for ):) because PEP 8 was not explicit about it.

If everyone agree, may I open a PR for this?

While I also find myself in agreement about the appearance of “):” on a line by itself being rather ugly, I’m somewhat concerned that explicitly adding it to PEP8 might further encourage the misconception that PEP8 is intended to be a styling guide for all Python code; when its main target is the standard library.

That being said, after a git grep of python/cpython, I found a few matches for “^):” in the following files:

Lib/test/support/__init__.py:959
Lib/test/test_ssl.py:47
Lib/test/test_type_comments.py:94
Lib/wsgiref/simple_server.py:152
PC/layout/support/appxmanifest.py:313

(Multiple matches in the same file were omitted for brevity)

In my opinion, if the content of PEP8 is not sufficiently clear, then the content should be explained better. If that can be best achieved by additional examples, these should be added.

I disagree that content clarifications should be put off to prevent scope misunderstandings: if the scope of PEP8 is not sufficiently clear, then the scope should be explained better.

1 Like

In general, I agree with the sentiment, but only if there’s genuine confusion around the topic relative to standard library development. I uncovered a few examples in the above post. The vast majority were in a single test file (Lib/test/test_type_comments.py), with only a single instance of it occurring in actual standard library code (Lib/wsgiref/simple_server.py line 152, which predates the GitHub repo).

Personally, I don’t think there’s an issue with the way the scope is currently addressed in PEP8. The very first line clearly states:

This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.

In the final paragraph of the intro section, there’s also:

Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.

It’s my understanding that the misunderstanding of its scope mainly just comes as a result of it being the most well-known Python styling guide.

To elaborate a bit though on my reasoning for not wanting to include an explicit example of the single line “):”, I’m not convinced yet that it’s a source of genuine confusion or a real issue within the context of stdlib development. If we end up adding the clarification solely because there was some confusion in other projects, especially for two that are specifically for general-purpose code formatting, I think it would add further to the misconception that PEP8 is intended to apply as a universal Python styling guide.

There’s also something to be said about not including every possible example of what to do and what not to do in PEP8. Covering everything would be virtually impossible, and attempting to do so would likely result in the document becoming over-encumbered with well-meaning, but minimally relevant examples.

1 Like

@aeros I think we generally agree. I say

if the content of PEP8 is not sufficiently clear

and you basically say it is sufficiently clear. (You explain that by referring to existing code; I just wonder if it would make sense to make sure PEP8 will be clear enough for contributors of new code. But apart from that, agreed.)

Also, I say

if the scope of PEP8 is not sufficiently clear

and again you basically say it is sufficiently clear. Again, fair enough.

I am in general OK with leaving PEP8 as it is, assuming content and scope are clear. I just don’t see “we don’t touch content or otherwise scope will be misunderstood” as an argument that I can follow. But I feel I am already repeating myself, so these are my two cents :slight_smile:

Best of luck to everyone for reaching a consensus!

Putting my 2 cent’s worth on a long and old chain.

I do think PEP-8 clearly rejects ): tabbed in on trailing lines. Perhapse some would like it if PEP-8 said something different and are searching for loopholes to get away with their view. But from what I read, the spirit of PEP-8 is clear enough on this topic.

My opinion is that ): is wrong for two reasons:

Reason 1: Visual interpretation

In Python, white-space is structurally significant. That literally means that when you tab right, you say to the reader “this thing starts here”. When you tab left you say “this thing ends here”. When navigating code quickly, this is important because it lets you skip over a lot without reading and without interrupting the eye.

There’s a clear difference between “}” tabbed left and “):” tabbed left. And it’s this: In a dictionary the definition starts where you tab in and ends where you tab out:

foo = {
    # Thing has started
    'x': 1,
    'y': 2,
} 
# Thing has finished. 

But when you do the same for a ): you break this rule:

def foo(
   # thing has started
   bar
# thing has finished ... oh no wait...
):
    # ... thing is still going ...
    pass


# ... now it's finished
def baz():
    pass

You never break this rule anywhere else in python or its style guide!

If this was a repeating pattern… if every ): appeared on it’s own line and tabbed back left, your eye will quickly learn the shape. But because this is being used in an exceptional circumstance it has a tendency to trip up they eye every time it occurs. I personally find this slows down reading code by an order of magnitude.

Reason 2: This appears to be against PEP-8.

It would be unreasonable to expect PEP-8 to explicitly ban everything anyone can think of. So it’s not supprising that there’s no “do not…” in this case.

But PEP-8 does give a tool box of how things should look for certain circumstances. But there is a conflicting “do this other thing”. PEP 8 states (clearly enough).

# Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

PEP 8 has told you definitively how to “distinguish arguments from the rest”. Unless you have a really good reason to deviate from this, then you can’t really ignore it and still claim to follow PEP-8.