297 comments

[ 3.1 ms ] story [ 292 ms ] thread
I'm pleased to announce that Black is finally non-beta software! :party:!

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!

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).

(comment deleted)
I think one of the great benefits of Black is that it is opinionated. Giving folks the option to select the type of indentation blunts the benefits. If you have a huge code base which mixes tabs and spaces it’s going to be hard to diff and merge code (or even reuse code snippets).
The point of black is that you run it on the same codebase with the same parameters.

Prettier works the exact same way and does have --use-tabs as a parameter. Nobody died. No codebase ended up with mixed tabs and spaces from it. Codebases either do --use-tabs or don't.

Like I said, there are a lot of reasons to allow for this. For one thing, tabs are an accessibility feature, but also it's impossible to use Black in an environment that prefers tabs.

Whereas there's no such thing as "an environment that prefers exactly two spaces after every comma inside tuples", thus you don't need an option for this.

https://prettier.io/docs/en/option-philosophy.html

>Yet the more options Prettier has, the further from the above goal it gets. The debates over styles just turn into debates over which Prettier options to use. Formatting wars break out with renewed vigour: “Which option values are better? Why? Did we make the right choices?”

>And it’s not the only cost options have. To learn more about their downsides, see the issue about resisting adding configuration, which has more s than any option request issue.

>So why are there any options at all?

>A few were added during Prettier’s infancy to make it take off at all. >A couple were added after “great demand.” >Some were added for compatibility reasons.

Reading this doc it feels like the Prettier team is saying options like tabs/spaces were only added early on to get initial adoption, if it were up to them now there would be far less options to configure at all.
Yes, that's right.
The options in question are some of the more.. fancy ones, and the "history" in question is things such as cross-compatibility with ESLint. Certainly not "tab support". (And I've contributed to Prettier a great deal, FWIW)
Unfortunately Black goes with PEP8, and PEP8 recommends spaces. Guido made this call arbitrarily years ago for absolutely nonsensical reasoning (basically came down to that some people use shitty editors that don't handle tabs reasonably) and the entire Python community has had to suffer since.
On the contrary, I think it's fundamentally useful to the ecosystem that there be a winner in the tabs-vs-spaces/how-many-spaces debate. Code snippets become portable, developers don't need to adapt when joining a new team, etc. Indeed, https://www.python.org/dev/peps/pep-0008/#indentation has enshrined 4 spaces as the official recommendation. And when the best-in-class formatter enforces this, that's a good thing.

To be sure, I personally would have preferred that 2-spaces win out for compatibility with the Javascript ecosystem (so I am perhaps the furthest from the parent poster on the tabs-spaces spectrum!) but I abandoned my preference in favor of PEP-8 years ago, and doing so has opened far more doors for team productivity than it's closed.

Environments that prefer tabs just use Tan, or otherwise, those environments just didn't use Black at all.

It's not useful to the ecosystem because those projects not actually using Black and unable to make the switch end up in a worse scenario (hence the fork which at least fixes this). Nobody wins, here; at best, you're unaffected.

Again I invite you to look at Prettier which has had as much of an impact (if not more) on the JS ecosystem as Black did on Python, but does support tab indent (and is opinionated regardless).

The Prettier team has gone on to say they regret giving people several of the configuration options they did though and have a page dedicated to their option philosophy. https://prettier.io/docs/en/option-philosophy.html
Yes, you'll find no mention of regretting adding tab support to prettier on that page.

Key quote: --arrow-parens, --jsx-single-quote, --bracket-same-line and --no-bracket-spacing are not the type of options we’re happy to have.

That's because these are entirely stylistic choices. Tab support is a mechanical one. Worth mentioning as well that Prettier supports multiple languages and tab/spaces is a global option, whereas all of these are very language-specific.

I don't want to wade in or start a debate, but just share my experiences with formatting over the years. Lacking autoformatters in the past, I've worked with coworkers that preferred 2, 3, 4 & even 8 spaces for indentation.

My personal preference is tabs for indentation, spaces for alignment. The reason for this is the customizability that comes with tabs instead of spaces. Any editor worth using for coding has an easily configurable tab stop that you can set to your desired space count. With tabs and proper editor configs, everyone can be happy with how their code looks while remaining absolutely identical with no tools reformatting on checkout or commit (as I've sometimes seen).

For a long time, I was in the 4-space indentation camp. These days, I prefer 2. The reality is, though, I use whatever the codebase I'm working on has as "standard".

Since Black enforces that indents are exactly four spaces, you could configure your editor to render four spaces at the start of a line as two spaces, getting exactly the behaviour you desire from tabs.

An editor plugin for this wouldn't even have to be syntax-aware, except for some rare cases where you are using spaces to nicely align a multiline string.

“Since black enforces four spaces, all you need to do is reinvent the functionality of tabs”

I know it’s very far from the biggest problem in the world but this does make me chuckle a little.

The proper number of spaces seems to me to be dependent upon the font size. Smaller font, more spaces.

In the olden days, with fixed width fonts on an 80x24 terminal, I think I read somewhere that 3 spaces was optimal (fewest bugs); but that programmers have an aversion to non-multiples of 2. That is why you see 2- or 4-spaces, but not 3. (I think...)

While I completely agree with all your other reasoning. It should be pointed out that PEP 8 is not intended to be a styling guide for user code, it's intended to be a styling guide for the standard library: https://www.python.org/dev/peps/pep-0008/#introduction

It's nice the broader Python community generally agrees with PEP 8 but it's always a little weird to see it referenced as an authority on styling user code when it is not it's intention and does not claim to be.

One thing I liked about Python was that PEP8 existed and was some sort of fairly aged standard for formatting. I’m not sure why people feel under deviation to suit their personal preferences is more valuable than consistency.
Consistency is not in and of itself valuable. What is valuable is when it makes things clearer, or otherwise easier. Tabs vs spaces has absolutely no effect on my comprehension of the code. Being able to use spaces for alignment would actually help in some cases but oh well, that's not allowed in python. The way black splits [] expressions is actually harmful when it's used in a chain with method calls as is not uncommon in pandas.
to anyone with even the slightest levels of OCD,(like say 95%? of software developers) consistency for the sake of consistency has a lot of value. The reason I like Black is not because of the authors opinions, but because it is consistent (due to the absence of configuration/user preferences).
I've had various issues with PEP 8, but mixing underscores and camelcase (in particular) has never sparked joy for me.

And pylint seems to keep adding style checks that I don't like and have to disable.

But the main principle I do agree with is to strive for consistent style within a code base.

Perhaps my naivete is showing, but I fail to see how an option that allows broader range of use cases suddenly becomes a religious war about spacing.

In the spirit of dev/user freedom, the creator has every right to enforce a standard, regardless of its basis - even if arbitrary - but I find it a little creepy. Then again, the prevailing usage of “opinionated” in dev circles was new to me, as well. What ever happened to design around maximum flexibility AND feature coverage?

Are developers more likely to be “opinionated” in their work if the predominant digital culture of their early career was rewarding of evangelism via self-promotion? Or grew up in an educational era that promoted activism? Are these sorts of issues more common in smaller / person-driven teams vs. corporate behemoths?

e.g., if VS Code were a one-person show vs. a corporate effort, would we risk seeing changelogs like “Insiders 2.20 - lead Architect and Face of the Product removes plug-ins starting with vowels, “because they lacked cohesion and product-centered aesthetics. They looked poopy in list format with most fonts.”

Absurd, to be sure, but what if these decisions don’t step on obvious toes? The average supporter is more likely to tolerate slightly warmer water than hop into another pot, right? And if you stayed in the pot through several degree increases, you’ll feel a sense of Boiling Frog Belonging (TM).

And if you credited the original dictatorial decision for this emergent sense of community, you’d be very much correct.

On the contrary, I want to thank the authors of black for not adding tab support.

I’ve been writing Python code for two decades. I know all the arguments for tabs vs spaces.

Down the path of tabs lies madness.

If you want them that badly, please see git’s smudge/clean filters.

https://stackoverflow.com/questions/2316677/can-git-automati...

There. Now the Python world has a single standard while you are free to use tabs in your checkout. See, you can make all the people happy all the time. :-)

At the risk of repeating myself, I forked Black into Tan and just added --use-tabs. The alternative was "don't use Black". Others are in my situation as well and use Tan, somehow finding it despite it not being advertised anywhere. Hell I had people bugging me to update it a few weeks back.

There's extremely clear demand, I didn't just add a random flag to control how much space should be around parentheses. Switching an existing codebase using tabs to spaces is not always feasible.

That said, TIL about a lot of what's in your link, but it looks like a completely inappropriate solution, I think you can agree.

I shared that link in good faith, not aware of your specific use case. I still think it's for the better that black does not allow tabs, and that your fork is a good solution for shops that need tabs for an existing code base.

Black encourages the Python ecosystem to settle on spaces. There should be some friction involved (more than a switch) to use tabs, otherwise we're likely to see new code using tabs too.

$0.02 and all that.

Sorry, I re-read my comment and noticed it sounds way more aggressive than I intended it to be! I love the little hack actually, but it looks like it's really just a hack ;)
PEP-8 is the de-facto standard for python. Why would a formatter for python support anything (such as tabs) that deviates from that? There might be code that does not adhere to PEP-8, but to me this is not a justification, just some people distancing themselves from the python core community.
Have you read PEP8? :)

Tab codebases are PEP8 compliant. It merely says spaces are "preferred". What it disallows is mixing the two. What it also says is to use tabs if the codebase uses tabs, which I can't do with Black; how about that.

Incidentally, PEP8 takes a much stronger stance on line length, says they should be no more than 79 characters, and Black has enough sense not to respect that by default (and... offer an option, because line length, much like tabs for indent, is an accessibility feature).

And I’m a little sad to see it’s gone stable without adding support for single quotes...

Wait I’m not. As much as I dislike double quotes, if they add an option every time someone is a little sad, we’re just going back to square one. I don’t want to debate styles anymore, ever, and I’m willing to give up my own aesthetics for that.

Just means I can't use it for work, since it turns a 1 line change into a several hundred line commit (all our code has single quotes).

These 'opinionated' formatters are great as long as you agree with the opinions they have. But the inflexibility makes them useless, otherwise.

And one commit converting all single quotes to double quotes is not an option?
> support for tabs

Tabs are not PEP 8 compliant except for consistency with existing code:

https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces

But if you're going to use a tool like Black in the first place, you're already committed to not preserving different formatting styles in existing code. You want to enforce one consistent style everywhere in the code base. And PEP 8 says that means no tabs.

> a personal grudge against tabs

I don't see why there would have to be any personal grudge given the above.

88 character lines are always pep8 non-compliant :)
Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the nominal line length from 80 to 100 characters (effectively increasing the maximum length to 99 characters), provided that comments and docstrings are still wrapped at 72 characters.
I know. That’s my point. Religiously following pep8 is absurd.
Congrats to the black team on this release, and thank you scrollaway for your fork. We are in a similar position where we are (for various reasons) stuck on using tabs for an existing project. Luckily it seems like there might be some movement on this front where the maintainer team is at least more receptive to reopening this conversation:

https://github.com/psf/black/issues/2798

Dude, what part of "you can have it any color as long as it's black" did you NOT understand?
> The default style is updated yearly.

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!

Congratulations to the Black authors! It's a wonderful tool, probably the first one I install when creating a local Python development environment.
This is exciting! Can it now be a plug-in for Prettier?
> Black prefers [i.e. converts] double quotes (" and """) over single quotes (' and ''').

Can someone explain this to me? Why would you ever prefer " over ' in a language where both can be used equally?

I prefer them too. Makes it easy to notice than single ones. It's just a personal preference, there's no big logic behind it.
Because "don't" is easier to read than 'don\'t'
And 'do not' is easier to read than both :)
Black will use single quotes if your string contains double quotes and no single quotes. It'll reformat

     print("\"Hello\" he said")
to

     print('"Hello" he said')
I think pretty much all formatters for languages that let you use either quote do this. It makes sense.
When you’re embedding SQL in a string, a you use ‘ a lot more than “. Not the only use case, but one to give some consideration.
I think that’s only MySQL. Postgres uses double quotes for identifiers and single quotes for literals.

EDIT: I misinterpreted that single quote as a backtick. In any case, both single and double quotes are common in SQL, but single quotes are a bit more common.

If you use proper apostrophes instead of ASCII apostrophe / single quote, you don’t have to escape anything and you get better typography. "don’t" is easier to read than both.

If you’re on a Mac, Option-Shift-] will type it.

A couple of possible reasons:

* Double quotes are more visible, especially for triple quotes.

* It makes it easier to have text without having to manually escape apostrophes.

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.
Except that in the language you're most likely to mix with Python, SQL, strings are in single quotes (double quotes are for identifiers like column names, especially if they're case sensitive). So it's probably a bad choice on Black's part.
(MySQL, notoriously, uses double quotes for strings.)

However, does not Python and standard SQL using different quote characters make it easier? I.e.

  query = "SELECT column_a FROM a_table WHERE column_b = 'foo';"
compared to this, if single quotes were enforced:

  query = 'SELECT column_a FROM a_table WHERE column_b = \'foo\';'
If it's useful to use the same string literal character "as everyone else", it would be useful to use the same as a language you are very likely to see combined - so, single quote.

On the other hand, if it's useful to have minimum need of escaping, then single quote is still better, because in SQL it's much more common to quote identifier names than to have literal strings.

  query = 'SELECT "columnA" FROM "aTable" WHERE "columnB" = \'foo\';'
compared to

  query = "SELECT \"columnA\" FROM \"aTable\" WHERE \"columnB\" = 'foo';"
> it would be useful to use the same as a language you are very likely to see combined - so, single quote.

Why? It’s obvious that it causes issues as I showed, and yet you simplay claim the opposite without argument? The difference between other programming languages and SQL is, of course, that it’s very common to have embedded SQL code in your program, but not common to have embedded strings of code in another programming language.

> in SQL it's much more common to quote identifier names than to have literal strings.

That is certainly not true. The kind of SQL you quote is only found in auto-generated SQL by ORMs and the like, and those are not likely to exist as strings in code. To use your example, this is the normal SQL way to write that:

  query = "SELECT columnA FROM aTable WHERE columnB = 'foo';"
I.e. no double quotes needed; no need to escape anything.
Using double quotes makes it easier to have single quotes inside the string literals.
It's a tic for me too, as I always prefer '.

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.

(comment deleted)
This was decided very early in the project, the discussion can be read here: https://github.com/psf/black/issues/51
Did you lock that issue before posting it here? Is this some kind of sore/forbidden topic for the project?

Edit: I suppose the question is superfluous seeing how many replies there are just here.

It’s easier and faster to differentiate between “ and ` than between ‘ and `.
This is simple: the default is picked, so now no one has to argue about it.
Arguing over Black formatting choices is a little ironic, isn’t it?

The whole point is that they’re all arbitrary so just let the defaults win, and adjust yourself instead.

The presence and depth of the debate about this formatting choice suggests that it isn't arbitrary. Many have provided solid reasons for why one option should be preferred over the other. They aren't merely articulating their opinions; they are also providing objective reasons for why their opinion is valid.
Everything about formatting is arbitrary, as long as it's consistent in a given codebase to the point of predictability (e.g. 90% consistent, not necessarily 100% consistent).

To elaborate a bit, sure, there are arguments for everything talked about here, and they're valid, but the marginal utility of any one choice is dwarfed by opting out of the arguments entirely, and just going with whatever Black has selected as a default.

In some languages single vs double has different functionality.
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.

Try nero from PyPI instead, I’ll upgrade to stable shortly.
Last commit was from 2019? It sounds like you're not automating the patch for every black release...
Things were factored a few times, wasn’t a consistent patch. Also I didn’t care for a few of the changes so didn’t upgrade yet. However stable should be a good target for automation.
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.

The other way around (converting double quoted strings to single quoted ones) would be more understandable, as that is what str.__repr__ prefers:

    >>> "string"
    'string'
I sense a bloated ego of the author in Black's decisions -- to contradict with the language's preferred formatting style.
This is a great tool. I wish there was some settings to use single quotes instead of double quotes.
There probably is, if you use another formatter that isn’t Black.
There is: -S, --skip-string-normalization Don't normalize string quotes or prefixes.
IIRC, the -S only avoid single quotes to get switched to double quotes. What I meant was allowing Black to switch double-quotes to single quotes automatically.
Funny that this is a common reaction against opinionated tools: "I wish there was a configurable option to apply my opinions"

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."

"My opinion is the correct one"

- Author of some tool

“No opinion is correct, so we should stop wasting time debating it.”

- advocates of code formatters

"let's everyone stop debating it the exact moment after you adopt my preferences"
I understand that. And this is what I like in Black. But the single quotes thing is really up to debate. When you use it with other tools such as flake8 or pylint, you have to disable a bunch of things to make them work together because no one agrees on this point.
I'm not a full time Python dev so I won't (can't) get into what choice has more merits. But there is this:

https://black.readthedocs.io/en/latest/the_black_code_style/...

Seen from outside, I think the double quotes are just the natural form of strings in lots of other places, at least in those that come from the same family than C (C++, Java, C#, Rust, just to name a few)

Also Prettier.js (which I'd call the "Black of JavaScript") also settled on double quotes. So we're left with a de facto consensus across programming languages as a whole, which kind of feels nice.

> When you use it with other tools such as flake8 or pylint, you have to disable a bunch of things to make them work together

Using Flake8 with Black requires only two configuration options [1], while Pylint requires three [2]. If you prefer to use Black with --line-length 79, then it's down to a single configuration option for Flake8 or two for Pylint.

[1]: https://black.readthedocs.io/en/latest/guides/using_black_wi...

[2]: https://black.readthedocs.io/en/latest/guides/using_black_wi...

That made me wonder, so I went and tested edge cases, like "abc\"d\"ef", which black quite properly converts to 'abc"d"ef'. So it does use single quotes where appropriate.
You can have some basic configurable stuffs in an opinionated tool though, see Prettier. It allows tab/space, quote style, line length etc., to be modified.
Try nero from PyPI instead, I’ll update it to this stable release shortly.
It's now enabled by default in IPython, for better or worse: https://github.com/ipython/ipython/pull/13397
That will be reverted in the next version, i.e. it'll be optional.
“the next release will likely revert it”

— Matthias Bussonnier, https://github.com/ipython/ipython/issues/13463#issuecomment...

Great, I'm sure it will end up with a good solution eventually. Unfortunate that it had to be this heated about this change.

R. Hettinger needed to learn to always be graceful when commenting or criticizing the work of others, especially volunteers.

And we all saw how wild the difference is in engagement for a project like ipython between the "normal" level and the "viral" level. Users are just using it and depending on it, never interacting with the project, but if something attracts attention it can be like a thousand flies flocking to it. Often when something gets negative attention..

There are several main personalities in the python-dev space:

1) People who easily get passionate but are gentlemen when it counts. R. Hettinger is one of these.

2) People who get passionate and are politicians. GvR is one of those and he is above the CoC.

3) Vicious politicians who always remain calm. These are the most dangerous, are often mediocre and climb the ladder at various large corporations. Some of these have not contributed much.

4) Some really nice people. You generally won't see much of them in discussions.

R. Hettinger is independent and honest, unlike the snake pit that runs Python.

After this comment the submission dropped from place 25 to place 56 in less than 10 minutes. Suppression of opinion is a standard PSF politician tactic.
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.
Here here! Same.
NB: It’s “Hear, hear”, not “Here, here”.
I love it even though I dislike quite a few of its formatting rules.

Because the only thing worse than a slightly wonk formatting convention is spending any time at all implementing, arguing about, or otherwise worrying about formatting conventions.

Word. I got major pushback trying to fix this problem at my last company, which is (in part) why I'm not there.
Rob Pike's proverbs about Go fmt springs to mind: "Go fmt's style is nobody's favourite, but go fmt is everybody's favourite."
gofmt, on the other hand, gives you a lot more flexibility to wrap lines how you deem appropriate. I like gofmt way more than black.
Exactly, gofmt is actually almost perfect. Ironically, it makes Rob's quote less meaningful.

I think gofmt's brilliance comes down to it not being overly pedantic, it doesn't have an opinion on how all code should be formatted, it only fixes what it sees as clear mistakes.

The problem I have with it is that, to me, these are super readable:

    x = [ 1, 2, 3 ]
    y = { 'a': 1, 'b': 2 }
Whereas these are not:

    x = [1, 2, 3]
    y = {'a': 1, 'b': 2}
To me, declaring inline lists and dicts without the leading and trailing space makes it more difficult to see what the data structure is, and what it contains. This is especially true when you have lists or dicts declared inline as arguments to functions, e.g.:

    my_function_call([ 1, 2, 3 ])
versus:

    my_function_call([1, 2, 3])
While it's generally not good style to declare data structures inside function calls, at least with the space it's still somewhat apparent what's happening, whereas without the space it just looks like three positional arguments. I love the idea of black, but in practice I'm not going to use something that changes my own code into something that's more difficult for me to read.
My only response to this would be “get used to it”.

I used to think 2 space tabs made code harder to read than 4. Now I used 2 spaces and I’ve gotten used to it and can read it fine. And I have no doubt you could learn to read the black formatted code just as easily. I can, since that is how I format my code anyway.

Agreed - end of day, code formatting is not that important in the context of how end users utilize the product.

Read a lot of different code and programming languages. They all look pretty much the same after awhile, formatting and syntax.

> I used to think 2 space tabs made code harder to read than 4. Now I used 2 spaces and I’ve gotten used to it and can read it fine.

I might be in the minority, but it's an empirically testable proposition. Given the thousands of people taking Python quizzes for toptal, triplebyte, etc., it should be possible to see which version results in a lower time to answer or a higher accuracy rate.

I think it just doesn't matter anymore because almost all editors support code folding and visual markers for it, making it MUCH easier regardless of 2 vs. 4.
And that’s the reason everyone should use tabs. You like 2, I like 4, someone else one the team likes 3, 6, 8, whatever? No problem if tabs are used, everyone can set tabstop to their liking!
That’s gotta be a really unpopular opinion since I’ve never seen anyone write “[ 1, 2, 3 ]” instead of “[1, 2, 3]” in the last 10 years or so.

If anything I’m seeing the opposite trend with a few devs writing “[1,2,3]” so that large constant literals don’t overflow the line.

I've recently been working with an old C++ codebase that formats every function call like this:

  Foo<Bar,Baz> value = some.method (1, 2, 3 + 4) ;
  // or with no parameters:
  value = some.method() ;
I've never seen it anywhere else, and it took me a good week to get used to it.
You might not have been writing JavaScript. For objects, spacing properties from curly brackets is pretty much the norm, Prettier does that by default. For lists I see it rarer but I personally use it instinctively for consistency with objects, especially with things like useState destructuring and ([ complex, ...splatting, ...combinations ]) into function calls (which admittedly becomes uncomfortably terse).
FWIW, I do "[ 1,2,3 ]". So we exist, perhaps you just haven't bumped into us :-)
Just learn how to make them readable. I am yet to see a single developer who didn’t manage to learn that. Seriously, the worst types are the ones who think they are experienced enough to think what “is more readable”. It’s all a matter of habit.
I disagree: your style is less readable than Black's. You've moved the data-structure syntax away from the data, and toward the function-call syntax, causing them to blend together.

This would be more readable -- put the space around the structure, not inside it:

    my_function_call( [1, 2, 3] )
Now we can clearly see the separation of the argument from the function call. But I wouldn't bother switching code formatters just to have this.
Agreed. My only grievance with Black was that it was really slow compared to gofmt or rustfmt, but that’s a perennial problem with Python tooling.
This is now improved. Black is compiled with mypyc, should be roughly 2X faster than before.
Is there a way to pass hooks with a repo, or do you have to give people instructions about how to install them and hope they do it?
Not directly, unfortunately. Sometimes you can hook it into the tooling, like the Makefile or the test runner.

Other than that, enforce it in CI: patches that don’t match the code style can’t be merged

Before I introduced Black in my workplace, all code reviews were peppered with stuff like "should be indented", "use single quotes" etc. Made it impossible to talk about the stuff that actually matters.

When introducing it there was some resistance. There's always that one guy who tried it once and it reformatted that perfectly hand-formatted function. My answer to them was suck it up. In a tiny fraction of cases Black might make a suboptimal choice, but the rest of your code is so shit it outweighs this negative by 100x.

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.
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.
Do it all in one commit.

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`

Thanks, that is helpful, esp I can google that to get more docs. One issue is everyone in the team needs to do so, but its probably worth it.
Is there an equivalent for TS/JS? How about Java?
Prettier is the closest I've found for JavaScript.
Prettier is equally amazing indeed.
Check out Prettier
“Standard” was popular for a while but fell out of vogue. Prettier is the one to reach for these days. I’m a big fan
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!

To add onto this, I also found that Black works as a nice heuristic indicating to split up code when the formatted output isn't "pretty" into separate lines.
It's been at least a decade since I've not used an autoformatter on every piece of code ever. I didn't realize there were people who worked at places without autoformatters still.
Had a similar experience when I used rustfmt (which led me to trying black). I was used to formatters that over formatted, removing newlines used to dilineate sections of code, etc. I tried rustfmt when learning Rust because I figured id learn adopt standard practices.

It was revolutionary. I went from formatting as I went, even for prototyping to throwing code at my editor and letting rustfmt fix it. Big difference in productivity.

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.

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.

To clarify, the slow git push was likely custom pre-receive hooks being slow.
`another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style` hahahahaha, I love that test case.
When I switched the company to Black, I reformatted the whole repository history with it. Every developer had to clone the repository again and reapply their local changes to it, but in the end it was a good choice because "blame" still works perfectly since we don't have one big reformatting commit.
looks like a `break` is missing after `has_special_comment` becomes True
Still ugly. I hate looking at code mangled by this thing. Single quotes? Fluent interfaces? Go write Go if you want ugly code you don't have to think about.

Don't @ me.

Pretty sure Black prefers double quotes in the main case, but what are “fluent interfaces”? Go is pretty great though, I completely understand the envy! :)
Does Black inhibit method chaining? In any case, I don’t see the value in fluent interfaces—if I had to choose between an opinionated code formatter and something people do because Martin Fowler told them to do it, I would take the former every time.
Sure, but why would black make those hard? (It doesn't, in my experience).
Yeah, and I wasn't aware there was anything to format. This is Python after all, code style is enforced by compiler lol.
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! :-)

> 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'm personally glad to see Python development come to the same conclusion enforced by Go's opinionated tooling.

That is: style questions are almost always unimportant to producing value, but teams still waste excessive time on them because of the engineering tendency to bikeshed triviata.

I think the general consensus of many people, myself included, is that it doesn't matter what the specifics are, as long as they're being applied consistently. Beyond that, it's a question of optimizing the cost/benefit ratio.

The problem with most things that have to be applied manually is that they're also applied inconsistently. It's like Hungarian notation. Over time, it becomes pure noise: you can't rely on it, especially when reading someone else's code, so it's safer to just ignore it. At which point, the convention's cost/benefit is approaching infinity, because its denominator nearly zero. So I'm not sure I care if the absolute cost is small. It's still an almost complete waste of effort.

The only place I've ever seen manual conventions applied consistently enough to be useful in a sustainable way is when someone's carved out their own private silo of code that is mostly only touched by them.

> If you don't want to discuss "taste" questions, then just don't, it doesn't matter what option you pick.

Exactly, it doesn't matter what option you pick, as long as everybody picks the same option. Enter opinionated code formatters, because otherwise how do you do it?

> otherwise how do you do it?

Only reject obviously quite messy code, and only rarely allow formatting rewrites. Learn that anybody’s strong preferences for a specific style or discussion of such aren’t demonstrably beneficial for the bottom line of the business or customers.

This is a recipe for people spending time cleaning up files, back and forth, ad infinitum. Or, just letting the stubborn person win.
My point is you let people have their different styles, reject changes on lines where code isn't changed, and coach the style zealots to calm down. Changing a couple of bytes in a file? Don't change the style. Writing something new? Figure out what's best. Rewriting a big chunk? Make reasonable formatting choices.

Some programmers are zealots who have a hard time working with others, the solution isn't always to force the matter or acquiesce to the squeakiest wheel. A strong organization should be able to enable people do have their style and disable people from forcing their opinions on everybody or wasting everybody's time with low-value debate.

> My point is you let people have their different styles, reject changes on lines where code isn't changed, and coach the style zealots to calm down

Code with style changes, especially within a file, is harder to follow than almost any reasonable consistent style. And adopting a common formatter with common settings is a lot less work than “reject changes on lines where code isn't changed, and coach the style zealots to calm down” even if it was neutral in readability.

> Changing a couple of bytes in a file? Don't change the style. Writing something new? Figure out what's best. Rewriting a big chunk? Make reasonable formatting choices.

This is exactly the type of arguably useless effort we don't want to spend any time on. Having to think about what is "reasonable", "allowed", or "best" is non-negligible cognitive burden for both the writer and reviewer/reader.

When I use black or other formatter, I have to exert cognitive effort to anticipate how the formatter will format the code so that I can avoid the inevitable stupid formatting decisions.

I often then tweak the code in a way that is not necessarily better, it's merely rendered better by black.

It just feels like I'm fighting against the tool half of the time.

I think it's a tell when people get so fussy about style, if you can't get past non functional differences how are you going do on differences of functional differences. "my code is spaghetti and performs poorly, but at least I used a formatter!"
> 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.

I think the appeal is that it avoids the discussions entirely (i.e. if it doesn't matter what option you pick, why even have the option? Particularly if these options will mean you divert away from the standard python style guide, which has been trying to standardize these choices for years so that codebases have similar rules.)

i.e. if your org has inconsistent python formatting between different independently-operating teams, it's easier to enforce PEP 8 than start a load of pointless arguments about what good looks like.

I can stop discussing taste issues, but it doesn’t prevent others from debating what the proper style for my PR ought to be, meanwhile I can’t merge my PR.
> I think you can convey meaning with subtle code style differences.

Yes that's very true, but, and now comes the big but - if you're working in a project with dozens and dozens of other devs, you will necessarily see all kinds of weird formatting. And this is where an opinionated formatter shines. It doesn't matter how many people work on your project, what the churn-rate is or if the guy that just started yesterday already read the code-formatting guidelines or not - the formatter will just take care of it and remove all hassles surrounding that topic. And this only at the price of opinionated formatting which I may or may not like - a price that I likely have to pay either way, because there's no way that everybody shares the same opinion about formatting in the first place.

I recommend to give opinionated formatters a try, if you haven't already. I know plenty of folks who were initially against it but eventually came around it because it made life a lot easier: No more discussions about formatting on PRs, no more tabs vs space wars, everything's already settled and automatically taken care of.

> Personally, I don't understand the appeal of opinionated code formatters.

Opinionated code formatters mean that opinionated coders with different preferences on a team don't produce (or at least produce less) noise in diffs.

I only reformat code I am rewriting, for that reason. I've never seen a tool that was very good at bending its own rules to minimize these accidental changes (e.g., let a function call go to column 83 to keep it on one line).
The reason I like black is the most opinonated folks have WEIRD opinions.

You see that here.

Things like a = [ 1, 2, 3 ] when 90% of the world is a = [1, 2, 3]

These folks will spend hours / days try to force weird approaches.

Sometimes they are obviously wrong. But it's annoying to have to fight over it. Something like black saves you the hassle and the WEIRD local coding rules someone comes up with if you are on a project with them.

Terrific point I never thought of. There's definitely a power law, where 80% of the arguing time is spent on the 20% dumbest shit.
I once came in on a project where one dev used "func( arg )" and the other used "func (arg)". They would reformat each other's lines when they changed something, so the diff had a lot of whitespace changes. The two were not on speaking terms any longer, so I was tasked with cleaning up the mess.

First step was to run an autoformatter. I used stock presets so neither of the two would think the other was favoured in the cleanup.

If I worked there I would always do func( arg) or func(arg ) just because.
Haha. Brilliant :) The extra spaces look so weird to me, but yeah, folks have strong opinions.
I just don't see why you would care if this extra space is in there or not. If they want to stick it in there, be my guest. I can read either just fine, even if, gasp, it's not 100% consistent through the whole codebase.
Will you change to match their code style? Great.

However, if another person ALSO has a strong opinion about this - nightmare.

> convey meaning with subtle code style differences

Why? This is exactly why we need autoformatters :-) if you're on a team, you should be conveying meaning with variable names, comments, and structure. If it doesn't show up in the syntax tree, it doesn't have meaning. Consistency always wins because it elevates everyone's ability to read the code.

There's more to it than the social aspect though: putting effort into manually moving around characters in a text editor is not the best use of mental bandwidth. My experience (and that of many I know) is that formatters help you code faster with less bugs by letting you focus on the higher-level structure of the problem NOT on the minutiae of where each punctuation mark should live. If your code is syntactically correct, the autoformatter will make it look "right" and you don't have to think about consistent formatting at all. If it's not syntactically correct, you set your editor to autoformat on save and you get immediate feedback. So it's not just about consistency, it's also a productivity aid.

I understand having strong opinions about the "right" code style. Personally, I dislike the double quotes in black. So I type single quotes and let black do its thing on save. What I type does not need to be what I commit (so long as they both compile to the same AST)! I don't want to waste time debating formatting details in a PR, and I certainly don't want to interrupt my coding flow to manually perform the labor of shuffling ascii chars around when there is fully automated solution. I can't think of a single scenario where my personal style preference would outweigh the significant advantages I've seen from autoformatters.

> If it doesn't show up in the syntax tree, it doesn't have meaning.

Why did you use paragraphs to write your comment then? People read the code thats written.

Auto formatters support paragraphs, but they don't support I'm going to give this paragraph 2 blank lines because it's particularly important/complicated/blah.
Oh interesting thanks! I couldn't find it in the docs before
Yeah, I don't think it's mentioned because it feels like supporting spaces between words, I’d guess. Just so fundamental a functionality it's assumed.
Typographic standards typically don't, either. Fortunately, there's more than one way to convey emphasis.

Also, single functions that are long enough to contain something analogous to paragraphs are arguably not Pythonic in the first place.

For paragraph I just mean a series of 1 or more lines separated by a blank line vs no blank lines.
Are there two blank lines between these parts of the code for a reason or did the person who wrote this code make a mistake?

If that part of the code is special for whatever reason, add a comment saying why.

I think the argument is not that structure should be dispensed, or that it doesn't matter, rather that a given style convention is fungible, and we should probably all just pick something good enough and get on with it, because the code will execute regardless of whether we use " or ' around our strings.

Spend some time considering the language standards, but then ship it for god's sake.

See also from Peter Norvig[0]:

* Get involved in a language standardization effort. It could be the ANSI C++ committee, or it could be deciding if your local coding style will have 2 or 4 space indentation levels. Either way, you learn about what other people like in a language, how deeply they feel so, and perhaps even a little about why they feel so.

* Have the good sense to get off the language standardization effort as quickly as possible.

[0]:https://www.norvig.com/21-days.html

I actually had this formatter thing happen at work. We have code that generates more python code. Now the quotes are changed and we need escapes because a rule it being applied. Escapes are harder to read and look messier.
> So I type single quotes and let black do its thing on save.

It is the one small grip, I have with black. C&P for example some string-indexed dict element as argument into a f-string sometimes produces duplicate double quotes if it gets into black's hands beforehand. But in the end, black outweights all these minor things. This particular issue also is probably better considered as a tooling task for editors, IDEs etc.

Sounds like you actually are very opinionated about your coding style, so it's both totally understandable and yet ironic that you'd begrudge that of others. I've tried hard to internalize that opinions are like assholes, everybody has one and they all stink. My own included.
> Empty lines to delineate blocks.

Use comments to describe those blocks and their meaning, don't just assume everyone is thinking exactly the same as you are about the code organization (hint, they probably aren't). Where you see a bunch of logically separated blocks of operations I might be new to the codebase and just see a bunch of unnecessary whitespace. Describe what's being organized and why in comments.

does it not allow you do something like this to set up variables?

  def foo(a,b,c):
    var x = 40
    var y = None
  
    bar()
    
    return x * b
I think the blocks of code make it more readable, some variables are set up, some work is done, then it returns something
Ye I don't understand it either. Why would you want some automated Whitespace Hitler telling you what to do. You can't even select between tabs and spaces for indentation.
Huge milestone for the Python community! Very happy for you guys. In Elixir, `mix format` has saved so much time across multiple projects and teams. I can't remember the last time I talked about line breaks and how to divvy up a long multiple line function header.
Hooray!

Loosely related - This is Python pip. Trail of Bits has a tool pip-audit that audits Python environments and dependency trees for known vulnerabilities.

https://github.com/trailofbits/pip-audit

Not that I don't appreciate the shoutout (I'm one of the developers of pip-audit), but what's the connection? Is it because black is installed via pip?
Here's the best thing about Black:

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.

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.
Unfortunately Black will reformat code which has only been indented/outdented, causing false diffs even with "ignore whitespace changes".
(comment deleted)
That's true. What I mean is it won't keep being reformatted when everyone runs their own formatter on the code before they start working on it.
I think every language should have such tool by default, like Go does.
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
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.
its the penultimate bike shedding issue.

If you're coders can code it doesn't matter, and if they can't formatting their poor code isn't going to fix it.

Just an off topic note on language: penultimate means second to (last, final, biggest, etc), i.e. the one before the ultimate. Perhaps an odd choice of word in context that is often misunderstood.

It leads to the question “then what is the ultimate bike shedding issue?”

yes, ironically the word choice is to avoid bike shedding, if I said it was the ultimate issue there would be all sorts but but no, this issue, where as if it's the 2nd everyone can have their pet issue be most important and we can reach agreement on it and move forward while tabling the discussion of what is the ultimate issue.

Whenever you have one of these bike shed issues in a meeting, do this: Give everyone 5 minutes to say their piece, give everyone an opportunity to ask 2 follow up questions to anyone, and a 2 minute response. Go through everyones opinion, take a vote and move on. Then set a date 1 year in the future to review/change the policy.

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.

Yep, just agree on something, whatever it is, enforce it everywhere and move on. It’s not worth the time arguing. I love gofmt for this.
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.
Yep. Even if I personally hate a coding style, I'll stick to it religiously if it's what my team uses. A good coding style is one that everyone adheres to.
is it just me, or does the logo look _really_ similar to the ford logo?
It's meant to, as it references Henry Ford's "any color so long as it is black" quip.
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.

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

> that won't move imports in ways that potentially change behavior of the codebase

How can that be the case? Can't Python imports have behaviour that varies on the time of the day if they want?

An import could monkey patch a basic operation one day but not the next.

Absolutely! We're hoping that the vast majority of modules are good citizens, but we also know that the reality not perfect. That's why µsort allows you to configure a list of modules with known import-time side effects, and µsort will then treat those are barriers everywhere.

https://usort.readthedocs.io/en/stable/guide.html#side-effec...

Even better, you can overwrite the default importer to substitute your own with any behavior you want. Used in EVE Online to do their "hide the python code somewhere" scheme.
Interesting. Now that isort has support for black-compatibility, what’s the main difference with usort?
We've written a number of words on "Why µsort", including a comparison to isort: https://usort.readthedocs.io/en/stable/why.html

The biggest reason is µsort's detection of sortable "blocks" of imports, and only sorting within those blocks to limit breaking changes, combined with use of proper parsing and CST manipulation to guarantee valid syntax after sorting.

Very cool. I love the idea of one tool rather than two, I’ll check it out!
FWIW, there's one particular use case where the isort behavior is incredibly useful: in eval-as-you-type data science e.g. when using Hydrogen in Atom you type an import whenever you need it but that leads to a lot of duplication and imports scattered across the file, and it's nice that isort just moves that up top on every save.
That's something we're aware of, and something we've considered, but also potentially dangerous from a safety standpoint, due to Python's dynamic nature, and how much moving imports can affect runtime semantics. Any implementation of this would need to be aware of those dangers, and also be able to provide stable and predictable behavior. At the moment, it's not a huge priority given the availability of tooling to automatically add missing imports, or the limited impact of under-sorting in the context of notebooks.
(comment deleted)
We never agreed on the indents

I typed a hundred tabs

You go back to space and I go back to black

with these lyrics i might even like the song.

awesome reference!

I use black and love it— but only with 120 line lengths. The default of 80 is wayyy to low for the days of 4k monitors. The tricks it uses to split some things up onto new lines actually makes it less readable.
I agree. I've sometimes found Black's output to be on the borderline of unreadable with the default line length of 80. My team settled on a length of 120 (the only Black config item we changed) and it has largely, though not entirely, solved that problem.
As long as you can easily review code side-by-side with line numbers intact, you're good. The default was chosen for lower resolution 13" laptop monitors to be able to display the Phabricator diff page (think: Github PR review page) without having to wrap any lines.
I agree 80 is too low, but it doesn't have anything to do with 4k monitors, it's just that 80 is too short. Any longer than 120 is not readable imo. Luckily Black lets you change the line length so it's not an issue.
Maybe it’s different for other people but smaller text is much easier on my eyes with a 4k monitor.