Nesting long lists and sets using black formatter

Viewed 688

Can the python black formatter nest long lists and sets? For example:

Input

coworkers = {"amy", "bill", "raj", "satoshi", "jim", "lifeng", "jeff", "sandeep", "mike"}

Default output

coworkers = {
    "amy",
    "bill",
    "raj",
    "satoshi",
    "jim",
    "lifeng",
    "jeff",
    "sandeep",
    "mike",
}

Desired output

coworkers = {
    "amy", "bill", "raj", "satoshi", "jim",
    "lifeng", "jeff", "sandeep", "mike",
}
4 Answers

This is not possible, because the coding style used by Black can be viewed as a strict subset of PEP 8. Please read docs here. Specifically:

As for vertical whitespace, Black tries to render one full expression or simple statement per line. If this fits the allotted line length, great.

# in:
j = [1,
     2,
     3
]

# out:
j = [1, 2, 3]

If not, Black will look at the contents of the first outer matching brackets and put that in a separate indented line.

# This piece of code is written by me, it isn't part of the original doc
# in
j = [1, 2, 3, 4, 5, 6, 7]

# out
j = [
    1, 2, 3, 4, 5, 6, 7
]

If that still doesn’t fit the bill, it will decompose the internal expression further using the same rule, indenting matching brackets every time. If the contents of the matching brackets pair are comma-separated (like an argument list, or a dict literal, and so on) then Black will first try to keep them on the same line with the matching brackets. If that doesn’t work, it will put all of them in separate lines.

Some workaround for this nasty black functionality

The following code is invariant under black

#demo.py
coworkers = (
    {}
    + {"amy", "bill", "raj", "satoshi", "jim", "lifeng", "jeff", "sandeep", "mike"}
    + {"john", "hans", "silvester", "gringo", "arold"}
)
$ black --diff demo.py 
All done! ✨  ✨
1 file would be left unchanged.

please note:

  1. you need the empty {}
  2. Putting "john" into the previous set will cause the line break behavior, as the maximum line length (black defaults to 88) is reached
  3. put no trailing comma into your sets. More info under https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#the-magic-trailing-comma

you can temporarily deactivate black

# demo.py

# deactivate black temporarily
# fmt: off
coworkers = {"amy", "bill", "raj", "satoshi", "jim", "lifeng", "jeff", "sandeep", "mike"}
# fmt: on
coworkers = {"amy", "bill", "raj", "satoshi", "jim", "lifeng", "jeff", "sandeep", "mike"}

gets with

$ black demo.py

reformated to

# demo.py

# deactivate black temporarily
# fmt: off
coworkers = {"amy", "bill", "raj", "satoshi", "jim", "lifeng", "jeff", "sandeep", "mike"}
# fmt: on
coworkers = {
    "amy",
    "bill",
    "raj",
    "satoshi",
    "jim",
    "lifeng",
    "jeff",
    "sandeep",
    "mike",
}

good solution for lists/sets of strings

You can split a string by the , character. While this solution is not universal, it has a nice and compact representation as code.

The following code is invariant under black

# demo.py
coworkers = set(
    (
        "amy,bill,raj,satoshi,jim,lifeng,jeff,sandeep,"
        "mike,john,hans,silvester,gringo,arold"
    ).split(",")
)

black:

$ black --diff demo.py 
All done! ✨  ✨
1 file would be left unchanged.

Also the generation of the string from some variable is pretty straight forward:

>>> ','.join(['amy', 'bill'])
'amy,bill'
Related