psf / black

The uncompromising Python code formatter
https://black.readthedocs.io/en/stable/
MIT License
38.85k stars 2.45k forks source link

Automatic magic trailing comma behavior to split arguments/items onto separate lines regardless of line length #3715

Open alex-hunt-materialize opened 1 year ago

alex-hunt-materialize commented 1 year ago

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

I absolutely never want more than two list items on a single line, regardless of how long that line is. I can manually add a "magic trailing comma" to the final item to get this behavior for a specific case, but that's easy to forget and, when working in a team, not everyone will do this. If this trailing comma is forgotten, shorter lists will end up smooshing it on a single line, which makes for larger diffs that are more difficult to visually parse.

This is especially annoying for code generation tools, such as python's built in ast module, since it will never inject the magic trailing comma on its own.

mylist = [item1, item2, item3, item4, item5, item6]

should be reformatted as:

mylist = [
    item1,
    item2,
    item3,
    item4,
    item5,
    item6,
]

Likewise, this would also be nice for function signatures and calls:

ImportantClass.important_method(
    exc, limit, lookup_lines, capture_locals, extra_argument
)

should be reformatted as:

ImportantClass.important_method(
    exc,
    limit,
    lookup_lines,
    capture_locals,
    extra_argument,
)

Describe the solution you'd like

I want black to to automatically inject this magic trailing comma behavior for all lists above a certain item length, regardless of line length. Ideally the number of items would be configurable (ie: --automatic-magic-trailing-comma-items=2 would inject the magic comma for any list with at least two items). I suspect most people would want it set to either 2 or 3.

Describe alternatives you've considered

add-trailing-comma helps, but only does it for things that are already on a new line due to line length.

my_long_function_name_heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeere(
    arg1, arg2
)

becomes

my_long_function_name_heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeere(
    arg1,
    arg2,
)

Which is better, but it doesn't change

myfunc(arg1, arg2, arg3, arg4, arg5)

Additionally, it may require multiple passes of add-trailing-comma and black to reach a stable point, since they don't have identical rules.

Additional context

JelleZijlstra commented 12 months ago

I'm inclined to reject this. Turning on your suggestion by default would be a big change and most users would likely perceive the new formatting as worse in many cases. We don't tend to add formatting options, and I don't see a strong reason why we would want one here.

alex-hunt-materialize commented 11 months ago

Having each argument on a separate line allows adding and removing arguments with the diff showing just that isolated line. If you have several arguments on the same line, it is much harder to identify what changed. Nearly all of your users use version control and would benefit from this change.

cameron-emburse commented 3 months ago

It would be nice to have an option that automatically adds the trailing comma if a line is too long and arguments end up wrapping. I vastly prefer

class FruitMachine:
    def do_fruit_machine_things(
        self,
        apple,
        banana,
        coconut,
        dragonfruit,
        elderberry,
        fig,
        guava,
        huckleberry,
    ):
        pass

to

class FruitMachine:
    def do_fruit_machine_things(
        self, apple, banana, coconut, dragonfruit, elderberry, fig, guava, huckleberry
    ):
        pass

Especially for this reason:

Having each argument on a separate line allows adding and removing arguments with the diff showing just that isolated line.

But also for readability, particularly with kwargs.

That said, I would not want the option to force every item to a new line unless the line is too long (or if there's an intentional magic trailing comma).