yapf icon indicating copy to clipboard operation
yapf copied to clipboard

Line breaking behavior for function arguments with and without trailing comma

Open danijar opened this issue 9 years ago • 27 comments

Hello and thanks for YAPF. It's been working nicely for us, except for the formatting of long function arguments. Take a look at the example below. If a trailing comma is present, we would like to keep the them in individual lines. Without trailing comma, an option to break right after the opening parenthesis would be nice. This is PEP8 conform (as is the the YAPF output) and plays more nicely with with long function names.

Desired format:

a_very_long_function_name(
    long_argument_name_1=1,
    long_argument_name_2=2,
    long_argument_name_3=3,
    long_argument_name_4=4,
)

a_very_long_function_name(
    long_argument_name_1=1, long_argument_name_2=2,
    long_argument_name_3=3, long_argument_name_4=4)

YAPF output:

a_very_long_function_name(long_argument_name_1=1,
                          long_argument_name_2=2,
                          long_argument_name_3=3,
                          long_argument_name_4=4, )

a_very_long_function_name(long_argument_name_1=1,
                          long_argument_name_2=2,
                          long_argument_name_3=3,
                          long_argument_name_4=4)

danijar avatar May 10 '16 12:05 danijar

What may help you get further towards the desired output is specifying SPLIT_BEFORE_NAMED_ASSIGNS: False. When I add that, it gives this:

$ python -m yapf --style='{based_on_style: pep8; SPLIT_BEFORE_NAMED_ASSIGNS: False}' /tmp/x.py
a_very_long_function_name(long_argument_name_1=1,
                          long_argument_name_2=2,
                          long_argument_name_3=3,
                          long_argument_name_4=4, )

a_very_long_function_name(long_argument_name_1=1, long_argument_name_2=2,
                          long_argument_name_3=3, long_argument_name_4=4)

There is another option you can set that can get you further:

$ python -m yapf --style='{based_on_style: pep8; SPLIT_BEFORE_NAMED_ASSIGNS: False, DEDENT_CLOSING_BRACKETS: True}' /tmp/x.py
a_very_long_function_name(
    long_argument_name_1=1,
    long_argument_name_2=2,
    long_argument_name_3=3,
    long_argument_name_4=4,
)

a_very_long_function_name(
    long_argument_name_1=1, long_argument_name_2=2, long_argument_name_3=3,
    long_argument_name_4=4
)

Please let me know if that works for you.

bwendling avatar May 11 '16 07:05 bwendling

@gwelymernans Thanks, that works except for the second example, where the closing parenthesis should not have its own line:

a_very_long_function_name(
    long_argument_name_1=1, long_argument_name_2=2, long_argument_name_3=3,
    long_argument_name_4=4)

danijar avatar May 11 '16 12:05 danijar

Thank you!

danijar avatar May 12 '16 17:05 danijar

Why did you remove this functionality with your newest commit https://github.com/google/yapf/commit/311c08719451a0cdc3a578b360293ffabb28e0d6?

danijar avatar May 18 '16 14:05 danijar

The knob didn't seem to be useful anymore, because I could get the formatting you wanted here without it. Has it regressed?

bwendling avatar May 18 '16 15:05 bwendling

Maybe there was a misunderstanding what behavior we would like a trailing comma to cause. If there is a trailing comma, each argument and the closing parenthesis should have their own lines to easily edit individual arguments (regardless of line length and DEDENT_CLOSING_BRACKETS knob). Some examples to illustrate:

function_name(argument_name_1=1, argument_name_2=2, argument_name_3=3)

# Break even though this would fit into a single line.
function_name(
    argument_name_1=1,
    argument_name_2=2,
    argument_name_3=3,
)

a_very_long_function_name(
    long_argument_name_1=1, long_argument_name_2=2, long_argument_name_3=3,
    long_argument_name_4=4)

a_very_long_function_name(
    long_argument_name_1=1,
    long_argument_name_2=2,
    long_argument_name_3=3,
    long_argument_name_4=4,
)

Chaoste avatar May 23 '16 13:05 Chaoste

Yes, this behavior would be very nice, including the breaking before the first argument in the third example but that might be an unrelated concern.

danijar avatar May 23 '16 14:05 danijar

I see what you mean. This will require a knob, though. But should be relatively straight-forward to implement.

bwendling avatar May 23 '16 18:05 bwendling

Okay. I added a new knob SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED which should do what you want. Please try it out on top-of-tree.

bwendling avatar May 24 '16 07:05 bwendling

Hey, the closing bracket still has not its own line. We have DEDENT_CLOSING_BRACKETS = false so the bracket is in the same line as the last argument if there is no trailing comma. But if there is a trailing comma I want a newline for the closing bracket independent of DEDENT_CLOSING_BRACKETS.

You used my code for your test but removed this newline. I would say you want your code never look like this (independent of the setting): args,)

Chaoste avatar May 24 '16 13:05 Chaoste

For some reason, I thought that you didn't want the trailing bracket on its own line. :-/

Give it a try now.

bwendling avatar May 24 '16 17:05 bwendling

It works nearly for all of my code. Only the following lines

SETUP_REQUIRES = [
    'Sphinx',
]

turned into

SETUP_REQUIRES = ['Sphinx', ]

Chaoste avatar May 24 '16 22:05 Chaoste

This will be another change. Should this split happen even with a single-element tuple?

TUPLE = (
    'element',
)

bwendling avatar May 25 '16 06:05 bwendling

I think this makes only sense for lists. For single-element tuples, the trailing comma is not formatting but syntax.

danijar avatar May 25 '16 16:05 danijar

Are there any changes we can try out?

Chaoste avatar Jun 04 '16 17:06 Chaoste

Not just yet. I may be able to get to it in a few days...

bwendling avatar Jun 04 '16 20:06 bwendling

Would be perfect, thanks.

danijar avatar Jun 05 '16 08:06 danijar

I'm guessing this is the right ticket for this issue.

Given the following style arg in the command: --style='{based_on_style: pep8, column_limit: 120, dedent_closing_brackets: 1, join_multiple_lines: 0, SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET: 0}'

I get this reformat, which I would like to avoid:

     class Meta:
-        unique_together = (
-            ("user", "issue"),
-            ("user", "company"),
-        )
+        unique_together = (("user", "issue"),
+                           ("user", "company"),)

Using SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED set to 1 did not help.

Amazing tool and great work, thanks for any help.

silviogutierrez avatar Jun 06 '16 14:06 silviogutierrez

I added this commit, which does this for lists. Please give it a try.

To https://github.com/google/yapf.git
   93885e4..8e3434a  master -> master

bwendling avatar Jun 08 '16 08:06 bwendling

So with this yapf config:

[style]
based_on_style = pep8
column_limit = 120
dedent_closing_brackets = true
join_multiple_lines = false
space_between_ending_comma_and_closing_bracket = false
split_arguments_when_comma_terminated = true

I now get this:


     class Meta:
-        unique_together = (
-            ("user", "issue"),
-            ("user", "company"),
-        )
+        unique_together = (("user", "issue"),
+                           ("user", "company"),)

But also this:


-        self.assertQuerysetEqual(issues, map(repr, [issue1, issue2]), ordered=False)
+        self.assertQuerysetEqual(issues, map(repr,
+                                             [
+                                                 issue1,
+                                                 issue2
+                                             ]), ordered=False)

Both suggestions are kind of odd. I would expect both examples to stay as is, unless they are over the line length limit. But maybe we have a very particular in-house style. Thanks for the updates!

silviogutierrez avatar Jun 08 '16 15:06 silviogutierrez

I have the same problem as @silviogutierrez.

Chaoste avatar Jun 08 '16 15:06 Chaoste

Are there any changes we can try out? :)

Chaoste avatar Jun 17 '16 06:06 Chaoste

I've been on a vacation for the past week, so I wasn't able to focus on this too much. I looked at this a bit, and I'm not 100% sure what the best way to proceed here is....

bwendling avatar Jun 17 '16 06:06 bwendling

Maybe I'm not enough familiar with your code but at some point it should look like this:

# After doing newlines depending on DEDENT_CLOSING_BRACKETS
regex_trailing_comma = r"\([^,]*,[^\n]*,([\s]*)\)"
re.sub(regex_trailing_comma, ",\n)", code)

This regex works for the following examples (tested with regex101.com):

text(a,b,sdfsdf, )
text(a,subtext(b,c),d,    
  )

# These ones are not selected by regex
(a,)
text(a,b,c,d)

Chaoste avatar Jun 20 '16 12:06 Chaoste

Hello! I have been evaluating bringing in yapf to a few projects. I found this thread and realized that the this issue applies to my current adoption. Has there been any resolutions, updates, or work-arounds discovered?

malcolmgreaves avatar Apr 28 '18 04:04 malcolmgreaves

What are the current recommended knob settings for as little line breaking as possible (in function definitions and in function calls)?

Jasha10 avatar Nov 26 '20 22:11 Jasha10

Is this issue solved/feature implemented?

sebhahn avatar May 05 '22 19:05 sebhahn