Black: An uncompromising Python code formatter
github.com
github.com
When the language itself has an established convention, Black should follow that convention, not fight it. These two weird choices just generate needless churn, which is surprising as it seems Black has quite the opposite goal.
It's a shame, because all the other design choices in Black are pretty good!
The language itself doesn't have an established standard in terms of string quote usage. If it did, Black would follow it. What repr() does is a weak indicator and how the documentation is written is random, there was not only no enforcement as to which quotes to use, there wasn't even a recommendation. Black standardizes on double quotes since it has clear benefits whereas the other option does not.
What I like about the Black philosophy is that it wants to make code style _uninteresting_. People should think about other things, not formatting. That's a great goal. So it seems to me that the best style choice is the most _boring_ choice. The least creative, least novel way. It should try to avoid inventing new formatting algorithms.
What's the most boring way to format a string literal?
The way the language already does it. The way every Python programmer has already seen string literals formatted from the very first day they started typing things into a Python interpreter.
Even if half of us liked single-quotes and half of us liked double-quotes, you can guarantee that every Python programmer on the planet has seen and lived with strings that are formatted the repr() way, including the double-quote fans. You can't guarantee the opposite.
No one could fault you for doing it the repr() way. Blame Guido :) he made that choice decades ago, and everyone has already had to make their peace with it. It's a solved problem. For anyone writing a program that emits Python code, it's the default way to format a string. It's the least assailable option, and that's a good thing.
How does that sit with you?
http://www.grymoire.com/Unix/Quote.html#uh-1
"When you need to quote several character at once, you could use several backslashes. This is ugly but works. It is easier to use pairs of quotation marks to indicate the start and end of the characters to be quoted. Inside the single quotes, you can include almost all meta-characters"
So some of us associate single quotes with proper const strings, with no variable expansion, command substitution or other interpreter hanky-panky.
I get your specific comment may be a bit sarcastic, given the ancestry - but I make this one more for younger developers I see that don’t always filter that ;)
Quotes: We have a difference of opinion over what constitutes an established convention. If the language's own way of displaying strings has been stable for its entire history, I consider that established. A reasonable choice would have been to do what repr() does (single-quotes unless the string contains a literal single-quote) or a simplified version of it (single-quotes always), not the opposite of what the language itself does.
(I wouldn't care so much about this except that I've really wanted a tool like Black for a while and greatly appreciate the philosophy!)
I can't speak for the readme, but:
$ cat test.py
x = [1,2,3]
print(x[1: 3])
$ black test.py
reformatted test.py
$ cat test.py
x = [1, 2, 3]
print(x[1:3])I tried out black and it does the following (the file originally had no spaces):
x = a + b
x = m[a:b]
x = m[a + 1 : b]
x = m[a:-b]
x = m[a : 1 - b]
I would personally still write m[a + 1:b], I think, but black's approach is totally defensible. (I guess I would really write m[a+1:b], and black would rightly correct me.)I usually avoid this problem by adding extra parentheses:
m[(a + 1):b]
f(x=(y + 1))Spacings that don't conflict with precedence are m[a+1:b], m[a+1 : b], or m[a + 1 : b]. While the middle one makes precedence obvious, all three are acceptable. black picks the latter, and I think it picks that one because of another rule that it should prefer writing a + 1 instead of a+1.
Double quotes are also the convention in english to delineate a literal, so I would argue it's more obvious.
I'll grant you keyboard presses though. There's an obvious advantage to single quotes here, but consider why this is the case. To make the use of "apostrophe" more efficient since it appears far more often than a double quote in english. I suppose it's pragmatic to leverage this advantage in code where string quoting is extremely common...
Perhaps double quoting is a bias I've developed writing code, but I suspect it's actually a bias I carried over from reading and writing english and what simply seemed more obvious.
Language design than supports both makes me crazy. Pick one and enforce it!
The visual noise complaint is interesting. Do you also consider the letter W to be more noisy than the letter V? Should we discourage the use of noisy letters in the alphabet?
The difference is that " and ' are equally usable options in the context we're talking about. Quotes are very common, so the visual noise adds up when your screen is full of quote marks. Given that they mean the same thing, and one is both harder to type and harder to read, it makes sense to prefer the other.
Nailed it.
I thought the same. I guess you could just modify your programming font to make the double quotes really tiny :)
raise KeyError('"%s" not found.' % name)
f'The flange is elevated by {flange["spronge"]} degrees of spronge.'
I don't normally use many contractions or possessives in my code, but am willing to admit it happens occasionally.
I'd like «guillemets» but that's going to have to be self-serve...
set_color('black','white')
And double quotes for strings meant to be read by a human: print("Hello there. How are you?")
And an example with both forms: syslog('warning',"unit %s spin rate %f too low",u,rate)
For me, it's an indication of how I expect the string to be used.PHP does treat single and double quotes differently. Contents of single quotes strings are not parsed for variable substitution.
This leads to the following difference between the PEP 8 recommendation and Black:
PEP 8 accepts both of these:
ham[lower+offset : upper+offset]
ham[lower + offset : upper + offset]
While Black just uses: ham[lower + offset : upper + offset]
Also, this is one that PEP 8 doesn't have a example on, but I think this would be one of the cases in which the space wasn't necessary: slice[a.b : c.d]
The "." operator binds so tightly in my mind that it doesn't need the spaces around the ":" to disambiguate. It's at the same precedence level as subscription and function call, and higher precedence than unary + and -.After typing this all out, I think that it might be the case that the bigger difference is actually in how binary operators are treated. It looks like Black always puts whitespace around binary operators, which contradicts PEP 8's recommendation that you are allowed to vary spacing around binary operators to make precedence clear.
Since Black does allow for leaving in extraneous parentheses to make precedence clear, I wonder why it doesn't allow varying the space around binary operators as well? Of course, it should enforce that the spacing actually does match the precedence, and that the spacing is consistent within an expression. That would allow the following examples which PEP 8 lists as "Yes":
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
While right now Black rewrites them as follows, which PEP 8 lists as "No" (though my reading is that varying the spacing like this is optional, so the following could be accepted as well): x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)With this in mind, it has to enforce a rule around operators. Since any operand might be complex, it's more robust to default to spaces around operands always. Otherwise we would inevitably end up hugging operands with an operator that humans consider too tight. And since that's subjective, there is actually no rule that we can hard-code about that.
Why not? I mean, I never built a formatter so idk about the complexity involved, but to me it seems that if Black had a rule like "if you see `a+b` (no spaces), just leave it alone", applying the rule twice shouldn't change anything.
(though I imagine that the interactions of such rules could become hard to understand...)
I’m probably going to still move to black but I know I’m likely to face some pushback now on this one choice, and I don’t yet have the will to defend it.
I also know I’m being mostly unreasonable .. “why is this opinionated tool not perfectly aligned with MY opinions??” .. but feelings.
key['first_name']
print("Isn't this clearer?")1. Any string literal that contains either ' xor " will be delimited by the other one. 2. For all other string literals, delimit by " if it's meant for human eyes only, delimit by ' if it's meant for computers as well.
Adopted that from Erlang, where atoms are single-quoted and text strings are double-quoted.
> It will replace the latter with the former as long as it does not result in more backslash escapes than before.
And I think that's good enough (and in compliance with PEP8)
[1] https://prettier.io/ [2] https://github.com/facebook/hhvm/blob/master/hphp/hack/src/h...
Thanks
I worked on prettier myself because I wanted to solve formatting for the language I was involved in. It turned out that the prettier infrastructure was actually really good for other languages so we used it for CSS, Markdown, GraphQL... and added support for a plugin system for other people to build printers for their own language. patrick91 (not working at Facebook) is working on a python formatter using the prettier infrastructure.
Independently, ambv (working at Facebook) started black which is written in Python. He's part of the Python core team and the Python infrastructure team at Facebook so it made sense for him to drive adoption of black within Facebook.
One interesting thing I realized is that communities are built around programming languages and it's really hard to influence another community from the outside. So my bet would be that black has the most chance of succeeding within the Python community.
IMO, that point is worthy of a detailed blog post or conference talk, if you would be so inclined. Would love to hear more.
When I started working on React Native, I thought that the most difficult thing would be to design a good set of APIs to make it easy to write mobile apps using React that felt good. This turned out to be the "easy part", we started the project wanting to solve this and having lots of good ideas on how to do it.
What turned out to be a lot harder was the fact that we were trying to use JavaScript from within iOS and Android ecosystems.
1) Those at the time were in different repos, how do you synchronize code between them?
2) The three ecosystems use a different set of tools for everything: IDE (xcode, intellij, sublime/atom/code/emacs), package manager (cocoapods, maven, npm), linters (eslint), build (how do you hook up with the play button in xcode?), profilers (can you display stack traces with the two languages calling each other?)...
3) Mixing and matching languages inside of a single project is hard because there are a lot of subtle different semantics (eg: javascript doesn't have int32 or int64). If you have type systems, they are incompatible (flow vs obj-c). So in practice you end up with a lot of boilerplate to talk between the two languages and it's a performance overhead.
There's also a social aspect where you invested so much learning an ecosystem that it becomes part of your identity. So you see someone wanting to bring another language as trying to attack you directly.
My mission since then has been trying to "break down the silos" and trying to build tools that can work with all those languages. It's not been easy :)
We tried YAPF before and could never roll it out for everybody. I even contributed the "facebook" style to the tool. There were a few reasons why YAPF didn't work out for us but the most important were:
- YAPF would at times not produce deterministic formatting (formatting the same file the second time with no changes in between would create a different formatting); Black treats this as a bug;
- YAPF would not format all files that use the latest Python 3.6 features (we have a lot of f-strings, there's cases of async generators, complex unpacking in collections and function calls, and so on); Black solves that;
- YAPF is based on a sophisticated algorithm that unwinds the line and applies "penalty points" for things that the user configured they don't like to see. With a bit of dynamic programming magic it arrives at a formatting with the minimal penalty value. This works fine most of the time. When it doesn't, and surprised people ask you to explain, you don't really know why. You might be able to suggest changing the penalty point value of a particular decision from, say, 47 to 48. It might help with this particular situation... but break five others in different places of the codebase.
The authors have been adding specific workarounds for some cases but it's a general issue with the approach:
- https://github.com/google/yapf/issues/264
- https://github.com/google/yapf/issues/39
Black algorithm doesn't explode in such way. It's also a lot faster overall which makes it possible and reasonable to enable a good format on save experience.
Except for the line length:
> if you're paid by the line of code you write, you can pass --line-length with a lower number.
Everyone would like to stick to the standard formatting rules. Well... except for that one little idiosyncratic thing that they just won't sacrifice.
Then black comes storming in to enforce conformity. Well... except for that one little idiosyncratic thing the author of black just can't bring himself to adhere to.
Don't get me wrong. I'm a big fan of the approach in general. I use things like paredit-mode and aggressive-indent-mode and whitespace-mode and I've just set up emacs to use black together with blacken-mode, but with blacken-line-length set to 80.
Let's call it "black-ish"
And for the 80 column fetishists, you'd have to pry their VT-100 terminals from their cold, dead hands. Disposing of the body is enough of a pain, but worse, you have to find a recycler who can deal with the lead glass in the CRT.
To avoid dying on that hill, many eng leads just throw up their hands and give into the 80-column folks, even if it's less productive for everybody else.
The beauty of gofmt and black is that formatting can become a commit hook, so no human wastes time cutting lines manually like some early 20th century typesetter.
The reason I always fight with people who want to increase line length is that they generally want to increase it to 100. Which is just a bit too big to fit two columns comfortably side by side at a reasonable size on a laptop screen, or three columns side by side on a wide desktop screen, which is how I generally set up my editors.
I'm always frustrated with codebases that use a 100 character standard, since I'm always running into lines that wrap, but when I use type annotations, 80 characters causes too many function signatures to have to wrap.
And my terminal happens to be around 240 characters wide for the font size I use and the size of my laptop's screen, which means if I limit to 80 characters or less I can snugly fit three files, or if I set to 100 I can get two with some breathing room.
Plus, if a line really is going over those lengths, then it's often a code smell: maybe I've got code that's too complex and ending up deeply-nested, or maybe I've got functions or methods taking way too many arguments, and hitting a line-length rule will warn me about that.
I am just being honest, and I think I am being helpful because other people will also react negatively to the way the documentation is written. I support the project and want it to be successful, providing feedback like this achieves progress towards that goal, in my estimation.
So far black seems great. I just ran it on some existing Python packages and it was fast and the output was correct. Still need to try the editor plugins but very excited so far.
Now, if only someone would lead a similar project for R. :)
A programming language has an opinion on how you build software with it, so it’s appreciated that it also has an opinion on how you should write it so that it remains consistent and easy to follow. No debate about where to put braces or semicolons or whatever else doesn’t matter when putting something in front of your users.
It feels like dumbing down in a way, which is sad, but I think this is more for the benefit of collaboration than individualism or artistic intent. In that case you either disable the tool or refuse to use it.
In every other case, you’ve automated away almost every nitpick from a code review.
That is a fantastic achievement.
Coming from that background I was initially put off by the lack of options with gofmt, but once I realized the whole world of go coders would be using the same formatting I immediately fell in love with it.
[0]: https://metacpan.org/pod/distribution/Perl-Tidy/bin/perltidy
- debate
- time to setup the tool to your liking
- testing (and adapting to the style)
- and going back to 1 from time to time
So at best it's going to be costly, morally draining and repeated regularly, especially if you change team or in open source. At worst, which is the case for perl, it will not be used by most devs.
The earliest implementation I know of was COMAL, a very clean and tidy version of BASIC, I think dating from around 1980.
A lot of 80s micro BASICs did some level of auto-formatting purely as a side-effect of the source code being stored in tokenized form to save memory.
Microware Systems Corporation's BASIC09 from about the same time took the code you entered and converted it into "I-code". It's VM code, Jim, but not quite as we know it. The basic09 program didn't do the things we expect from IDEs now, but it did let you modify code etc., so its internal form reflected the BASIC09 statements and control structures rather than having lower-level branch and conditional branch instructions. That let it prettyprint your code with a consistent format when you listed it. (It also let it avoid the insane interpretation and symbol table lookup overhead of Microsoft BASICs of the era, inherited from the days when Altair BASIC had to run on a system with 4K of RAM.)
Double quotes for strings that need to be human readable, single quotes otherwise.
This makes it so obvious when something is going to be sent to the user, we find it really useful. That said, I think Black’s appeal is it’s uncompromising nature, so I wouldn’t ask it to change. Adding the option to turn off quote formatting would probably go against its vision. Also, it could be argued that we should use the internationalisation functions to denote strings sent to the user, but hey we don’t do i18n yet.
For now, this, and one or two places that it fails to have an opinion (number of lines after imports) are keeping us from using it.
As for number of lines after imports, how is a lack of enforcement there stopping you from using the tool? Black enforces one line but is fine if you put two (on module level). In general, if you give up on the tool due to a missing rule, you end up having to manually enforce tens of other rules that you'd otherwise be free from.
That's a bit rich. There are other conventions in programming that you can't depend on technically but serve a real purpose. Identifier naming and comments are the first that come to mind.
If a language gives you a choice of token that has no semantic distinction then different people will adopt different semantics by convention.
As an aside, calling a tool "opinionated" is code for "my conventions are better than yours". That's fine if I don't have any conventions or I can't decide, but if I have decided, then it's just offensive.
So I don't read "opinionated" to necessarily mean "better than your opinions"; it's more like "makes decisions for you so you can avoid the cost of debating them."
But I can buy the argument that just having a single auto-enforced rule improves consistency and that has greater benefits than the somewhat vague distinction that is not enforced.
Yes. It's pretty much the only formatting style point that we don't have automated.
> As for number of lines after imports, how is a lack of enforcement there stopping you from using the tool?
Our current automated linting enforces it, but Black doesn't always reformat it, so we might get linter errors from Black formatted code.
Alright, fair enough!
> Black doesn't always reformat it, so we might get linter errors from Black formatted code.
Well, as long as it doesn't add new linter errors, that should be fine, do you disagree?
There are always going to be suboptimal formattings and missing transformations but as long as the situation gets better automatically on average and can be further improved with minimal manual input, you should be fine.
Then you also have the ambiguity of what is considered human readable, is an xml document human readable? Http headers? File paths? Urls? Is a programmer considered human?
Smells like non binary serialization format or something alike, which is usually a code smell. It's convenient the first 2 weeks but once the project grows you need a more strict schema and once you have that you might as well use a serialization library which might as well have a binary serialization backend.
Why would you not? E.g., you use pandas and columns all have names. Colors are typically also strings, etc. Thus you would often do things like
grouped = df.groupby(['foo', 'bar'])['baz'].mean()
However, the parent's point, IIUC, is that he'd do grouped.plot(color='red', title="User-facing title.")Then yes, there are valid uses, especially in ad-hoc scripts. Column names and dictionary keys are one of the gray zones, though again, in my experience once your project grows these are also usually better to code generate from your db schema or serialization protocol; either complete data structures, api-functions, or just a list of constants. Anyway, my point is not to ban strings entirely, it's to question what we use it for. If you use strings as data/identifiers so frequently that you need a special convention for them something smells quite fishy.
[0] Auto complete = More accessible form of documentation. Before someone starts screaming that i'm stupid for "ignoring documentation".
The most successful strategy was to add a flag in the file (@format in the header) to tell that a file is automatically formatted. The immediate benefit is that we enable format on save for developers on those files when they use Nuclide (>90% of penetration for JavaScript and Hack).
The other advantage is that when we release a new version of the formatter, we can re-run it on all those files so that people don't have lint warnings on code they already formatted in the past.
With that setup, there's a strong incentive for individual engineers to run the formatter on their team codebase in one PR and then everyone benefits from now on.
Sure, there is an additional step but we feel this shouldn't be a blocker for significant workflow improvements.
In fact, a single big "reformat all" commit is better than a bunch of incremental ones that reformat areas that you also change semantically. That is harder to filter and makes diffs harder to follow (which changes are logic and which are just style?).
> git hyper-blame is like git blame but it can ignore or "look through" a given set of commits, to find the real culprit.
https://commondatastorage.googleapis.com/chrome-infra-docs/f...
I don't agree with every detail--double quotes as default is going to be hard for me to adjust to--but the things I don't agree with aren't as important to me as being able to set it and forget it and stop debating it every so often.
This is the gofmt the python world needs. As far as I'm concerned this is the new standard.
Well done.
Either it gets caught in code review and you have to waste time with nitpicking, or worse it makes it through to the repo and now you have to make a commit to fix what amounts to a typo.
Autoformatting with a unified, consistent tool means that you remove all those problems.
That's the key part for me, too: Black offers the freedom of not needing to waste time talking about things which really don't matter. No more wasting time on code review where the real issues are obscured by sloppy whitespace, idiosyncratic formatting preferences, etc.
I also had a preference for single quotes but … every file in every project I work on is consistent as soon as I hit save and I certainly don't care enough not to let that outweigh a minor aesthetic point.
I may be in a minority, but I do not want to cede control over minutiae of hand-formatting. Am I the only person that feels this way?
The idea is to toss aside control over nitpicky formatting _configuration_ options in favor of not worrying about formatting configuration and just going with someone else's opinion of what the configuration should be instead.
José Valim, creator of Elixir and general programming whiz, I think perfectly summed why formatters are so great in a talk he gave at Elixir conf.
(I am paraphrasing from memory here so if someone has the source, please chime in.)
The gist: “I started using the formatter and at first I ran the it on my code and I hated it. It’s taking all my carefully, hand formatted code and messing it up! But then I ran it on OTHER people’s code and I loved it, as the code started to look like the standard format I had gotten used to.”
I think many people don’t start to like formatters until they see what it does to other people’s code. Many people like their own fine tuning, but that’s only half the question. For a big project, most of the code I read will not be my code. I prefer all of that code be in one, consistent style. Sometimes formatting is expressive, so it is a trade off, but for me the lost expressiveness is far outweighed by the Gaines consistency.
People are into things like this pep8 etc because they don't want to waste another second of their lives thinking about formatting. Or, worse discussing, arguing, bikesheding, documenting, enforcing, teaching the new guy how we format "here".
I'm sorry to sound snarky, but this is one of the things you slowly learn over years of development. I've had more than 25. Long ago I felt like you. No longer.
Personally, the only code formatter I've ever been really comfortable using is clang-format. And the reason is that they really try hard to get the corner cases right. Black might be fine, but I've been burned many times with other tools and in general would be reluctant to trust a tool like this without seeing what it does in practice to a large code base.
The larger the codebase and the more developers you have working on a project, the less important edge cases become and the more benefit you get from a common standard.
Looking through Black's rules, it seems to me like the rules it's implementing are comprehensive and specific enough that I'd probably end up with few, if any, situations where I even want to take control. And I'd gladly give those up in return for not having to wade through so much diff clutter when I'm doing code reviews.
It isn't really interest in minutia -- My fingers just do the thing automatically at this point; which means if autoindent is on, I then have to go back and delete all the stuff my muscle memory has made me do.
I'm ok with it if i'm forced into some IDE with an editor that is not built for actually writing code (i.e. every IDE default editor); in those instances auto-formatting is very useful. I just avoid those environments, if at all possible.
I'm willing to let go of the things I'm used to in favor of having something completely uncontroversial. I've been in the business not as long as you, but long enough to count the time lost on this stuff.
We do get attached to style and personalization. I think I was a lot more attached when I was younger at this. Perhaps my formatting was more important when my code itself was less personal or less elegant or something. Or maybe the tasks were simply things that weren't all that interesting but just needed to be done. So my way of leaving my mark was to make the formatting just absolutely perfect. Perhaps it was a way of asserting some agency in junior positions where the architecture was predetermined, the problem was well-defined, and the solution was already known when the ticket was assigned. Just get in there and write the code.
I think--and I may be wrong about this--that as I've gotten older and into roles that are more autonomous, where I get to architect entire components of core company business or start from scratch or do other things that assert my personality and agency in code, I care a hell of a lot less about formatting. Mine, yours, someone else's, I don't fucking care, just forget about it and move on.
I also suspect that caring a lot about code formatting is one of the few ways that juniors can signal that they are really engaged in their work and get a little attention. You can't argue about an application's design or anything actually important, so you push a little on what you can, which is somewhat reasonable, and probably a signal of poor management, really.
Anyway, I digress. Bottom line is that I care less and less as I get older and have other things to worry about. I'm starting to view people who are really picky about personal conventions of code formatting as people who either don't or can't contribute anything more interesting to a conversation.
"Gofmt's style is no one's favorite, yet gofmt is everyone's favorite."
(It just wants to be consistent, something the computer is very good at enforcing.)
C++ has clangfmt.
Black has a good momentum right now it very well might be the clear winner in a few months
I use clang format because it gives me full control over the style I cede control to it because it happens that I can express all of my personal minutiae of hand formatting in clang format rules. In contrast, I currently am writing Java in VS Code, and the Java formatting plugin doesn't give me an easy way to change its rules, so I disabled it entirely.
That is just not true. Where does Black not conform to PEP 8?
Hand-formatting. You are describing formatting code by hand.
Are you consistent with your formatting rules? I'd be interested to know how much time you spend formatting your code vs ceding control to an auto-formatter.
I'm inclined to think that the reason that auto-formatters are popular is not because manually formatting code is hard, but simply to head off nitpicking in code review.
I think there's a better solution to the "style nitpicking in code review" problem: Just don't do it.
If you're nitpicking style during a code review, chances are good that you are not looking for real problems.
But when working with other people, getting everyone to do the same thing, and have that automatically done for you / enforces is incredibly valuable. It’s such a massive win that any deviation from “my personal optimum formatting” is rounding error.
Admittedly, Black works differently; as I understand it, it will just auto-reformat your code rather than yelling at you and making you go through your code and fix everything by hand, which is what the aforementioned approach did.
Still, I wonder about the usefulness of such tools. Python code is already much more uniform than most other languages. Also, I am not sure you should take the last crumbs of creativity or personal preference away from programmers. Last but not least, PEP 8 is meant as a style guide, not as a book of law that needs to be enforced at all costs. Some of the Python core developers seem to agree; I have seen comments from Guido and others who apparently think that such tools go against the spirit of the PEP.
Trailing whitespace causes issues with git, editors, diff tools, and numerous other things, as well; keeping it out of a repository is a good thing.
> I had only one blank line between two class definitions
I certainly agree that that's the kind of thing a tool should help with rather than complain about and make you fix.
It's also the type of thing you should be getting your IDE to worry about. And not leave it till it's time to commit/push.
black .
or autopep8 -ir .
before commit, what's the big deal? Keeps the code consistent, improves readability, simplifies CI and allows focusing on more important things.On the other hand, it drives me nuts when I see stuff like this in our source files. I would love to have a commit hook that did nothing but enforce a minimal set of white space rules. Just requiring no trailing white space and no mixed tabs and spaces would make me so happy.
>Admittedly, Black works differently; as I understand it, it will just auto-reformat your code rather than yelling at you and making you go through your code and fix everything by hand, which is what the aforementioned approach did.
I don't actually write much Python, but surely you could have run the same tool (or some other formatter configured to match the linter) locally to have it do that auto-format for you?
Maybe... I don't work there anymore, but if I'm ever in a similar situation, I will consider that approach, assuming it will only reformat files as needed. (I suspect the company-mandated flake8 script scanned all the code (200K lines), rather than just the files that changed, considering how slow it was.)
A coworker of mine at a previous job used a JS autoformatter built into his editor, and he couldn't insert a `debugger` statement into his source when testing locally because his editor would delete it immediately...
I spend about equal amounts of time fighting with my style linter, formatting my code, and disabling dumb lint rules. Maybe an auto-formatter would save time by reducing the first two more than it would increase the second (though if the formatter introduces bugs by deleting bad code all bets are off.)
I also don't really care about style... Who really cares where line breaks are? Who cares whether you line up your comments with spaces or not? That stuff doesn't "take time", affect readability, cause arguments etc, because we're not children.
Globally, though, code is read an order of magnitude more times than it's written. So it's a huge productivity improvement in the not particularly long run.
(Of course you can make code less readable in other ways, e.g. by choosing undescriptive names, or weird idioms, but flake8/Black naturally don't address those issues.)
The only reason I don't use autoformatters is because most of them are bad. In my experience, black doesn't have those issues.
Actually PEP 8 starts with: "A Foolish Consistency is the Hobgoblin of Little Minds"[1] A lot of people miss that part.
At the risk of being redundant, I also raise an eyebrow at the choice to prefer double quotes for strings. My company standardized on single quotes, mainly to be consistent with repr and also encourage the use of double quotes in messages displayed to the user.
Everything else seems in order. I might increase the line width to 90 just to use an easier value to remember when configuring editors and other tools ;)
Judging from the additional stars on GitHub, and projects that just migrated (pytest!), I'd say there's a very vocal minority which is very attached to single quotes.
Accuse me of selection bias. Immediately use even more biased selection bias.
I thought dbl quotes smart for all the reasons in the readme. Me commenting 'this is great' is just noise on HN, and discouraged by the rules. Never take self-selected anything as truth, especially comments (tweets/posts/voluntary votes)
Exactly why every language, from here until the end of time, should have a “go fmt” equivalent.
left[first][second] = right[first][second][third]
Manual breaking looks like this: left[first][second] = (
right[first][second]
[third]
)
Code formatters will produce something like the following atrocity: left[first][second
] = right[first][second][
third]
Comments and strings are also unwrappable if the formatter is afraid of inserting characters. left[a_rather_long_key][a_rather_long_key] = right[a_rather_long_key][a_rather_long_key][a_rather_long_key]
Out: left[a_rather_long_key][a_rather_long_key] = right[a_rather_long_key][
a_rather_long_key
][a_rather_long_key]> Black ignores previous formatting and applies uniform horizontal and vertical whitespace to your code.
I now see it does sometimes modify non-whitespace characters e.g. later in the README it mentions:
> In [certain] cases, parentheses are removed when the entire statement fits in one line
I'm not in a position to test black out right now (I can't run Python on the computer I'm posting this comment on). I'd be curious to know what it does on the code I posted, and on over-length comments and string literals.
For your code example, see my comment above.
Then there were requests to add command line arguments specifically so that tools could integrate with black that were added almost immediately.
Congrats on gaining so much traction so quickly, and thanks for listening to users (when it makes sense).
General guidelines:
1. One commit with only the automatic formatting. Afterwards you'll be able to skip over it easily with `git hyper-blame` or `git blame $BLACK_REV^ -- $FILE`.
2. Avoid leaving open pull requests. If you do, after landing the blackening commit, blacken all pull requests, too. They shouldn't conflict then.
3. Set up enforcement with pre-commit or CI (you can run `black --check` on Travis or similar).
4. Don't forget the repo badge ;-)
Figured it out:
`autocmd BufWritePre *.py Black`
So we used 80. I always felt bad when the linter stopped people from pushing impactful changes because they went over the limit by two characters. So two years ago I set up a "highway speed limit" style warning in flake8 (code B950 in PyCQA/flake8-bugbear). What it does is it keeps your limit intact (for example "80") but doesn't trigger unless you went over by more than 10%. So the limit happens to be 88.
When I was working on Black, I was faced with a dilemma. Should the formatter stick to 80 or be able to "go over" a bit, too, as we would let humans do. I felt like the latter made more sense as the resulting code looks nicer (fewer occasions to break a single line into three or more). Then I remembered Raymond's talk "Beyond PEP8" where he mentions that experience shows "90-ish" is the wisest choice. So I went with it.
I saw that you mentioned that - where have you seen a study that claims 100 is the cutoff? Would be interested in seeing that.
Probably thinking of one of these:
- backslash continuations
- terminals/etc that counted newlines
- off by one errors
If your screen is larger than that it's no big deal, but if your screen is 80 columns and the cursor was at column 81, it would wrap to the next line without actually being a newline.
The old standard was actually 78, so that a diff would fit on the screen.
Like Raymond Hettinger said in his talk beyond pep8, 90ish is better than a strict 80. If you have 81 characters on a line its a waste of time to move that to three lines and now harder to read. So there should be some buffer. that buffer is 10%. The goal is 80 but we are ok with up to 88.
I'm sorry this makes using my tool harder for you but I'm sure there is an easy way for you to install Python 3.6 without destroying your system Python. There's Homebrew for macOS, deadsnakes for Ubuntu, EPEL for RedHat, and so on.
Someone needs to ask a StackOverflow question about Python formatting and we need to answer it with "Just use Black." Upvote the fuck out of it, and be done with this forever.
I usually prefer a set of dead-simple formatting/styling rule, easier to enforce, lower cognitive load.
And the two exceptions you mentioned are ones you will also have to make if you want to stay PEP 8 compliant (pycodestyle's E203 is invalid inside slices) and you want your code to execute on Python pre-3.6 (where you can't add trailing commas to calls and signatures containing args and *kwargs).
I think that's signature only. You don't have problems with calls:
$ python3.5
Python 3.5.5 (default, May 17 2018, 07:04:26)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo(*args, **kwargs):
... print(*args, **kwargs)
...
>>> foo(
... 'abc',
... )
abc
>>>
The argument black made about not adding trailing comma is also quite unconvincing to me:> Unnecessary trailing commas are removed if an expression fits in one line. This makes it 1% more likely that your line won't exceed the allotted line length limit. Moreover, in this scenario, if you added another argument to your call, you'd probably fit it in the same line anyway. That doesn't make diffs any larger.
Who cares about the 1% chance of not exceeding the line length limit? If you really care about that, use one-per-line style, not all arguments in one new line.
BTW, your signature doesn't demonstrate a call with unpacking. What you meant to test was:
l = [1, 2, 3] foo(0, *l,)
This works in 3.5+ but fails on 3.4 and before.
What linters have been doing is to figure out --some-- rules that are general enough that can be enforced.
Complete formatters like black are making decisions for --every single formatting choices--.
In practice, they need to be complex if they want to have people using them.
require([
"base/js/namespace",
"base/js/events"
],
function(Jupyter, events){
events.on('finished_execute.CodeCell', function(){
// execute black here
});
});
In ~/.jupyter/custom/custom.js.Use independent components, and let people use what they work best in.
https://github.com/JetBrains/intellij-community/tree/master/...
# in:
TracebackException.from_exception(exc, limit, lookup_lines, capture_locals)
# out:
TracebackException.from_exception(
exc, limit, lookup_lines, capture_locals
)
I don't like this one. I would prefer that if you have )/]/} on the next line, then you should have a trailing comma, e.g.: TracebackException.from_exception(
exc, limit, lookup_lines, capture_locals,
)
Also I would prefer one-per-line over all in the same line (but not on the same line with the parentheses), but I feel less strongly about that one.If this cannot fit in one line:
foo(arg1, arg2)
My first choice would be: foo(
arg1,
arg2,
)
Second choice would be: foo(
arg1, arg2,
)
While Black chooses: foo(
arg1, arg2
)
And made some unconvincing argument about it:> Unnecessary trailing commas are removed if an expression fits in one line. This makes it 1% more likely that your line won't exceed the allotted line length limit. Moreover, in this scenario, if you added another argument to your call, you'd probably fit it in the same line anyway. That doesn't make diffs any larger.
Who cares about the 1% chance of not exceeding the line length limit, really?
.editorconfig is a start but the amount of time saved through automation and removing customization is significant.
In contrast, when you're promising that this exact problem won't happen anymore because an automatic tool will handle stylistic preference for your team, people are more willing to accept stylistic choices they (mildly) disagree with. Because on average the style is still better and on average everybody can move faster.
In the ruby world for example, rubocop has an auto-correct feature. However it is only implemented for a subset of the style checks.
I have mine set to 120. I code my python code in Pycharm, on a high resolution screen. Having a column limit of 88 only makes sense if you are inside of VIM or something.
It's worth asking - does the standard make sense still, given how we edit today?
* Studies of readability generally show that it declines once lines of text are longer than 60-70 characters, not counting whitespace or punctuation. At that point, humans have difficulty finding the beginning of the next line, which slows them down. You can compensate for this by increasing line spacing but you lose a bunch of space that way. The vast majority of professionally typeset natural language material is limited to about this length, or even shorter.
* People read code in terminals. Most terminals default to 80 columns wide. Consider people that develop on multiple computers and multiple OSs, and have to reconfigure them all. Or if you use a new computer or loaner computer the defaults will be back to 80. So if you change to 120 columns, you have to do it over and over again. Same with text editors, but less so.
* Side-by-side diffs can get cumbersome if the text is more than 80 columns wide, and consider that font sizes vary, and some people like their monitors vertical for reading diffs so they can see more context. On my 24" 1920x1200 monitor, I can easily read a side-by-side 80 column diff, very nearly 100, but definitely not 120.
* As a heuristic, an abundance of wide lines often indicate problems with the code itself. Too much nesting or something like that. This depends on the language and indentation used, it's generally accepted that Java code will be something like 25% wider.
I'm not saying that 80 columns is the right choice, only that there are reasons to support that choice. Just like there are reasons to choose 100 or 120.