Work continues as usual with bugfixes and enhancements, but style changes are now introduced under our new `--preview` CLI switch. This allows us to evolve Black's style without too much disruption to users that want consistency. The default style is updated yearly.
Thanks to our maintainers for orchestrating the efforts, especially to our most recent reinforcement Batuhan (@isidentical) who was responsible for our match statement support! A hearty thank you to all of our contributors for pushing Black forward, and to our users for being the reason we do it!
Congratulations. I'm a Python developer of 17+ years and Black is truly a huge blessing in the Python ecosystem.
That said, I'm a little sad to see it's gone stable without adding support for tabs, which would be extremely simple to add at this point (cf. https://github.com/jleclanche/tan/commit/e23c038167528bdacdd...). I have a lot of people using this tab-capable fork, that I did not advertise anywhere.
Łukasz seems to have a personal grudge against tabs which may be why the issue for tab support was closed early on, but there's a plethora of good reasons to support it behind a flag. I don't want to rehash those arguments here on HN but you think you could re-think the approach a bit?
I'd be happy to do a PR if it's not getting rejected right away with "no discussion allowed" like the last one was (before Black was moved to PSF maintainership).
I'm hoping there will be minimal or even zero style based commits, excepting those related to new Python features. It wouldn't be very Black-like to force a commit of a potentially enormous size on users of the library every year. Probably something you're already thinking about.
I was apprehensive about taking our legacy codebase Black, but zero regrets. Thanks for your work!
I remember we migrated 2+ million LoC to being formatted by Black at Dropbox.
Our Livegrep instance with a custom Git blame implementation always crashed at the commit made to do the migration :-) We had to pause our merge queue because we didn't want to run into conflicts, and I remember the `git push` ended up taking a while.
Fuck I love Black -- makes working with other developers amazing once you stick it in a precommit. The quote from Dusty Phillips on the homepage is perfect and has stuck with me for years. I don't have to debate with developers about their individual preferences over what's best because we can just use Black and be done with it.
I'm not a big fan of automatic code formatters, unless they get somewhat more configurable. One simple example is line length. Usually a code formatter will break long lines, for example a function call with some arguments. But what if I have a log call there? Do I want to have that log span 3-6 rows, just because the silly formatter thought it is a long line? Well it is a long line, but I don't want to break it into multiple lines, as that would give that log call waaaay too much space. When the log call spans multiple lines, it distracts from the bits of code between log calls. Another example is, that these code formatters are often configured wrongly in people's code editors to reformat everything in the whole file. That adds lots of changes and people do not afterwards separate their commits for "only reformatting" and the actually important bits of their changes.
As PEP8 already says: "A Foolish Consistency is the Hobgoblin of Little Minds". An automatic code formatter is the epitome of consistency, as it applies the rules everywhere the same way. In many places it might give some benefits, but in others it will ruin the original code formatting. I am experienced enough to format my code in a readable way and I don't need it reformatted, just because someone has to try out some tool. Especially log calls. Those are a pet peave of mine.
Adopting Black made me realize quite how much of my coding thinking capacity had previously been spent thinking about code formatting - I used to really sweat the details about how to break up a long function call, where to put the line breaks, how to indent my dictionary literals...
With Black, I don't spend a single moment thinking about that at all. I estimate I've got a 5-10% productivity boost in my time-spent-writing-code from this!
One missed opportunity in Black's algorithm is that it currently treats the maximum line length as a literal hard limitation in number of characters. Here is a trivialized example:
to_add = [item for item in data.new_items if item not in data.old_items]
to_remove = [
item for item in data.old_items if item not in data.new_items
]
Although the constructs are nearly structurally identical, they can be formatted very differently, which sometimes hinders understanding them.
A different approach would be to instead normalize all words to a certain fixed width. So, "to_add" and "to_remove" would have the same virtual width.
A related issue is that leading indentation counts towards the width limit. This causes refactorings which simply move code around (changing its indentation level) to change the code's shape, even when the code hasn't otherwise changed. This is exacerbated by that one often needs to mold code in such a way that Black formats it in an agreeable way, but this is generally not done during refactorings, so the readability of the code suffers.
I had the opportunity to write a formatter (for SQL, also unconfigurable/opinionated); it seems to successfully avoid these problems: https://github.com/CyberShadow/squelch
Personally, I don't understand the appeal of opinionated code formatters. If you don't want to discuss "taste" questions, then just don't, it doesn't matter what option you pick. If it is an important question, then you should debate it.
I think you can convey meaning with subtle code style differences. Empty lines to delineate blocks. Single quotes if the string is a keyword, double quotes if it is for the user. Spaces around operators to make an expression clearer. I spend a minute or two before I commit to make the code tidy (linter and then manual tweaking) and would expect that from everybody on my team - it takes often less time than rebasing and picking good commit names, for example.
But even though it annoys me slightly when I encounter Black (or god forbid, Go) used in a project, I know a lot of people like it a lot, and it is good to have the choice. So congrats to the release! :-)
(Note: I mostly write Java these days, so my viewpoint is colored by this.)
Code is meant to be read by humans. Compilers don't have wetware eyes.
Homogenizing code that was hand-formatted for the situation ignores the art and craft of writing software.
I think formatters do have use as a "base" (e.g. Allman or K&R curlies?), especially for junior devs, but experienced developers that care about their software that has their name on it normally put their best foot forward. They would strive to present the most readable software they can put forth.
I've been writing software since 1981 and have yet to meet a code formatter that I like. `// @formatter:off` !
Python has significant whitespace, so you can't just click "ignore whitespace changes" in Github diffs and it doesn't matter. You have to put up with every silly whitespace change.
5 upvotes generate a pro tip: have your CI run black --check against the code you want formatted. That way different devs can run the formatting how they like (in tox; in a pre-commit hook; in IDE; on the command line) and CI just enforces that it happened.
I like Black for normal Python code, but it seems to mangle Pandas / Dask code at times. I still use it extensively cause it doesn't seem like there are other good alternatives.
I don't love how Black doesn't let you put some clarificatory parentheses -- for instance, they get wiped from
`eq_balanced = (left_hand_side == right_hand_side)`
But the benefits of never wasting time on discussing inanity outweigh any specific complaints.
Shameless self-promotion, as a former coworker of Łukasz, Black's creator:
Another coworker and I have created an import sorter that fits well alongside Black, called µsort. It is designed from the ground up to be a safe, stable import sorter that won't move imports in ways that potentially change behavior of the codebase, and without needing developers to litter their code with "skip" directives. We use it in our daily formatting codemods on tens of thousands of source files every day, and just finished our 1.0 release in December.
Going further, if you like enforcing both formatting and import order in your CI pipeline, I also created the project µfmt, which combines both Black and µsort into a single, atomic formatting step. This ensures there's never any conflict of opinion between the two tools, and any formatting changes are presented as a single diff result.
Long ago, I worked at a company that decided it needed coding standards. We spend two weeks arguing about coding standards. Nothing else got done.
Ever since then, I am not opinionated about coding standards. The benefits do not match the costs.
Although I do like the approach of "use something like black on precommit", and if you don't like it, you can reformat to your standards on check out, and be happy. It will get fixed on check in.
Sort of why I think Go got it right with gofmt. Its not as strict as it could be but all Go looks more or less the same and it’s because it was built into the language
To me, the point of using code formatters is that you don't have to think/talk about code formatting much at all. As in, there are never annoying comments about formatting in PRs.
A nice bonus is that you end up having uniformly readable code.
Too many people have too strong opinions on the subject. Some things are reasonably good ideas, many more are just arbitrary, strict rules prevent doing reasonable exceptions, and an enormous amount of time is wasted discussing the matter.
Arguing over coding standards is harmful, but consistency is a good thing. Best to adopt a standard style guide and limit any debate to deviations and even better to have a tool like Black make it automatic.
Because most commonly used languages use double quotes, not single quotes, for string literals. Some languages can use both, either interchangeably or with slightly different semantics, but others either uses single quotes for other purposes, or do not use them for anything at all. Therefore, double quotes for strings look more normal, so to speak, in source code.
That was the thing that bugged me at first, but other people on my team loved it. Some things bugged other people on my team, even though I love them. If a tool can manage to annoy everyone about the same amount, but in different areas, it’s doing something right.
And while it’s still kind of strange that `a = “foo”` looks different than `repr(a)`, after a while of using Black I don’t notice it anymore.
Additionally, standardizing all quotes makes searching for a specific string literal across a codebase easier, since you don't need regex to match against both quote types. Once you've decided to standardize strings, it doesn't matter much which one you pick.
Personally, I still type single quotes in almost all cases, and just let Black reformat to double quotes to save shift key presses.
Anyone have a foolproof way to reformatting all the code in a repo without screwing up history? I've seen some complicated commands which seem too sketchy to a git novice like myself.
I'm a huge fan of black, and have been using it in most of my projects for a long time.
That being said, my biggest gripe with it is that it has at some point started reformatting docstrings [0] in addition to the code -- strictly following PEP 257 [1] -- without any way to disable that behaviour. While I understand the desire to standardise, docstrings are not executable code, and should be allowed more flexibility when it comes to formatting.
[+] [-] crlees|4 years ago|reply
Change log: https://black.readthedocs.io/en/latest/change_log.html
Going forward we'll follow our stability policy (https://black.readthedocs.io/en/latest/the_black_code_style/...).
Work continues as usual with bugfixes and enhancements, but style changes are now introduced under our new `--preview` CLI switch. This allows us to evolve Black's style without too much disruption to users that want consistency. The default style is updated yearly.
Thanks to our maintainers for orchestrating the efforts, especially to our most recent reinforcement Batuhan (@isidentical) who was responsible for our match statement support! A hearty thank you to all of our contributors for pushing Black forward, and to our users for being the reason we do it!
[+] [-] scrollaway|4 years ago|reply
That said, I'm a little sad to see it's gone stable without adding support for tabs, which would be extremely simple to add at this point (cf. https://github.com/jleclanche/tan/commit/e23c038167528bdacdd...). I have a lot of people using this tab-capable fork, that I did not advertise anywhere.
Łukasz seems to have a personal grudge against tabs which may be why the issue for tab support was closed early on, but there's a plethora of good reasons to support it behind a flag. I don't want to rehash those arguments here on HN but you think you could re-think the approach a bit?
I'd be happy to do a PR if it's not getting rejected right away with "no discussion allowed" like the last one was (before Black was moved to PSF maintainership).
[+] [-] darkerside|4 years ago|reply
I'm hoping there will be minimal or even zero style based commits, excepting those related to new Python features. It wouldn't be very Black-like to force a commit of a potentially enormous size on users of the library every year. Probably something you're already thinking about.
I was apprehensive about taking our legacy codebase Black, but zero regrets. Thanks for your work!
[+] [-] ublaze|4 years ago|reply
Our Livegrep instance with a custom Git blame implementation always crashed at the commit made to do the migration :-) We had to pause our merge queue because we didn't want to run into conflicts, and I remember the `git push` ended up taking a while.
There was only one change that we had to make to Black to get it working on our codebase - https://github.com/psf/black/commit/024c9cab55da7bd3236fd887...
Glad to see it's now stable.
[+] [-] Mockapapella|4 years ago|reply
[+] [-] zelphirkalt|4 years ago|reply
As PEP8 already says: "A Foolish Consistency is the Hobgoblin of Little Minds". An automatic code formatter is the epitome of consistency, as it applies the rules everywhere the same way. In many places it might give some benefits, but in others it will ruin the original code formatting. I am experienced enough to format my code in a readable way and I don't need it reformatted, just because someone has to try out some tool. Especially log calls. Those are a pet peave of mine.
[+] [-] simonw|4 years ago|reply
With Black, I don't spend a single moment thinking about that at all. I estimate I've got a 5-10% productivity boost in my time-spent-writing-code from this!
[+] [-] CyberShadow|4 years ago|reply
A different approach would be to instead normalize all words to a certain fixed width. So, "to_add" and "to_remove" would have the same virtual width.
A related issue is that leading indentation counts towards the width limit. This causes refactorings which simply move code around (changing its indentation level) to change the code's shape, even when the code hasn't otherwise changed. This is exacerbated by that one often needs to mold code in such a way that Black formats it in an agreeable way, but this is generally not done during refactorings, so the readability of the code suffers.
I had the opportunity to write a formatter (for SQL, also unconfigurable/opinionated); it seems to successfully avoid these problems: https://github.com/CyberShadow/squelch
[+] [-] dang|4 years ago|reply
Black – Uncompromising Python code formatter - https://news.ycombinator.com/item?id=19939806 - May 2019 (244 comments)
Linting 400kLOC of Python Code with Black - https://news.ycombinator.com/item?id=18536731 - Nov 2018 (1 comment)
Black: An uncompromising Python code formatter - https://news.ycombinator.com/item?id=17151813 - May 2018 (255 comments)
[+] [-] captainmuon|4 years ago|reply
I think you can convey meaning with subtle code style differences. Empty lines to delineate blocks. Single quotes if the string is a keyword, double quotes if it is for the user. Spaces around operators to make an expression clearer. I spend a minute or two before I commit to make the code tidy (linter and then manual tweaking) and would expect that from everybody on my team - it takes often less time than rebasing and picking good commit names, for example.
But even though it annoys me slightly when I encounter Black (or god forbid, Go) used in a project, I know a lot of people like it a lot, and it is good to have the choice. So congrats to the release! :-)
[+] [-] altgeek|4 years ago|reply
Code is meant to be read by humans. Compilers don't have wetware eyes. Homogenizing code that was hand-formatted for the situation ignores the art and craft of writing software.
I think formatters do have use as a "base" (e.g. Allman or K&R curlies?), especially for junior devs, but experienced developers that care about their software that has their name on it normally put their best foot forward. They would strive to present the most readable software they can put forth.
I've been writing software since 1981 and have yet to meet a code formatter that I like. `// @formatter:off` !
[+] [-] robertlagrant|4 years ago|reply
Python has significant whitespace, so you can't just click "ignore whitespace changes" in Github diffs and it doesn't matter. You have to put up with every silly whitespace change.
Until Black.
[+] [-] robertlagrant|4 years ago|reply
[+] [-] CyberShadow|4 years ago|reply
[+] [-] MrPowers|4 years ago|reply
I wrote a blog post on how to use Black in Jupyter Lab notebooks if anyone is interested: https://coiled.io/blog/code-formatting-jupyter-notebooks-wit...
It's really nice to format a notebook with the click of a button.
[+] [-] EddieLomax|4 years ago|reply
[+] [-] faut_reflechir|4 years ago|reply
[+] [-] rsfern|4 years ago|reply
https://black.readthedocs.io/en/stable/the_black_code_style/...
[+] [-] hivacruz|4 years ago|reply
[+] [-] j1elo|4 years ago|reply
But the whole idea is that you should learn to suppress your ego and let the tool be the one dictating stylistic choices...
Like the sibling comment mentions:
Dusty Phillips, writer:
"Black is opinionated so you don’t have to be."
[+] [-] crlees|4 years ago|reply
[+] [-] mixmastamyk|4 years ago|reply
[+] [-] nopenopenopeno|4 years ago|reply
[+] [-] woodruffw|4 years ago|reply
[+] [-] jreese|4 years ago|reply
Another coworker and I have created an import sorter that fits well alongside Black, called µsort. It is designed from the ground up to be a safe, stable import sorter that won't move imports in ways that potentially change behavior of the codebase, and without needing developers to litter their code with "skip" directives. We use it in our daily formatting codemods on tens of thousands of source files every day, and just finished our 1.0 release in December.
https://usort.readthedocs.io
Going further, if you like enforcing both formatting and import order in your CI pipeline, I also created the project µfmt, which combines both Black and µsort into a single, atomic formatting step. This ensures there's never any conflict of opinion between the two tools, and any formatting changes are presented as a single diff result.
https://ufmt.readthedocs.io
[+] [-] _tom_|4 years ago|reply
Ever since then, I am not opinionated about coding standards. The benefits do not match the costs.
Although I do like the approach of "use something like black on precommit", and if you don't like it, you can reformat to your standards on check out, and be happy. It will get fixed on check in.
[+] [-] voidfunc|4 years ago|reply
[+] [-] keithasaurus|4 years ago|reply
A nice bonus is that you end up having uniformly readable code.
[+] [-] colechristensen|4 years ago|reply
[+] [-] surfmike|4 years ago|reply
[+] [-] dgellow|4 years ago|reply
[+] [-] formerly_proven|4 years ago|reply
Can someone explain this to me? Why would you ever prefer " over ' in a language where both can be used equally?
[+] [-] drekipus|4 years ago|reply
But to be fair I think it's because 1. Double quotes are ""universally"" strings in other languages 2. Having apostrophes gets annoying.
The great benefit really is that I can write single word strings just fine, and black will adjust it for me. I get my cake, and so do the others.
[+] [-] Teongot|4 years ago|reply
[+] [-] TameAntelope|4 years ago|reply
The whole point is that they’re all arbitrary so just let the defaults win, and adjust yourself instead.
[+] [-] teddyh|4 years ago|reply
[+] [-] ambivalence|4 years ago|reply
[+] [-] kstrauser|4 years ago|reply
And while it’s still kind of strange that `a = “foo”` looks different than `repr(a)`, after a while of using Black I don’t notice it anymore.
[+] [-] breput|4 years ago|reply
* Double quotes are more visible, especially for triple quotes.
* It makes it easier to have text without having to manually escape apostrophes.
[+] [-] indymike|4 years ago|reply
[+] [-] HALtheWise|4 years ago|reply
Personally, I still type single quotes in almost all cases, and just let Black reformat to double quotes to save shift key presses.
[+] [-] odiroot|4 years ago|reply
[+] [-] hadcomplained|4 years ago|reply
[+] [-] nopenopenopeno|4 years ago|reply
[+] [-] floober|4 years ago|reply
[+] [-] mixmastamyk|4 years ago|reply
[+] [-] faangiq|4 years ago|reply
[+] [-] rr808|4 years ago|reply
[+] [-] mumblemumble|4 years ago|reply
Then put that commit's (full) sha in a file named something like .git-blame-ignore-revs
Then `$ git config blame.ignoreRevsFile .git-blame-ignore-revs`
[+] [-] BerislavLopac|4 years ago|reply
That being said, my biggest gripe with it is that it has at some point started reformatting docstrings [0] in addition to the code -- strictly following PEP 257 [1] -- without any way to disable that behaviour. While I understand the desire to standardise, docstrings are not executable code, and should be allowed more flexibility when it comes to formatting.
[0] https://github.com/psf/black/issues/1779
[1] https://www.python.org/dev/peps/pep-0257/