Nesting long lists and sets using black formatter

Question:

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",
}
Asked By: irahorecka

||

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.

Answered By: Riccardo Bucco

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
Answered By: Markus Dutschke

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'
Answered By: Markus Dutschke

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",
}
Answered By: Markus Dutschke

This is the only pain point I have with Black. Everything else is flawless. My workaround is a bit convoluted, but here goes.

  1. I minify the long list using a Minifier, I use Python Minifier and I untick Hoist Literals, Rename Locals, and Rename Globals.
  2. I paste the one line into my Python file or a random Python file.
  3. I run yapf on the file to get the list nice and neat.
  4. Lastly I paste the tag fmt: off before the neat list and fmt:on afterwards.

Yes this is too much work, but at least it gets it done.

Answered By: Daniel Cárdenas