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",
}
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:
- you need the empty
{}
- Putting "john" into the previous set will cause the line break behavior, as the maximum line length (black defaults to 88) is reached
- 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
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'
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",
}
This is the only pain point I have with Black. Everything else is flawless. My workaround is a bit convoluted, but here goes.
- I minify the long list using a Minifier, I use Python Minifier and I untick Hoist Literals, Rename Locals, and Rename Globals.
- I paste the one line into my Python file or a random Python file.
- I run
yapf
on the file to get the list nice and neat.
- 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.
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",
}
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:
- you need the empty
{}
- Putting "john" into the previous set will cause the line break behavior, as the maximum line length (black defaults to 88) is reached
- 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
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'
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",
}
This is the only pain point I have with Black. Everything else is flawless. My workaround is a bit convoluted, but here goes.
- I minify the long list using a Minifier, I use Python Minifier and I untick Hoist Literals, Rename Locals, and Rename Globals.
- I paste the one line into my Python file or a random Python file.
- I run
yapf
on the file to get the list nice and neat. - Lastly I paste the tag
fmt: off
before the neat list andfmt:on
afterwards.
Yes this is too much work, but at least it gets it done.