848 karma · joined April 30, 2009
Contact information in the KeyBase link below.
[ my public key: https://keybase.io/ambv; my proof: https://keybase.io/ambv/sigs/YFlWGlv21O-p2KBCGkahQq5QvzaW2fYVzzXGYMEreM8 ]
$2a$11$70w6J.UbVsGwuTMIvSTq5Oh97csRvkT9bz2BuWASdYI1W4MhnMDU2
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.
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).
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.
Supporting an entire new AST and type system (pattern matching) is a large investment. It can be made if there's enough of a userbase out there to justify it. That doesn't seem to be the case. Not yet at least.
Arguments accepted in __init__() are not necessarily tied to the so-called "shape of the type" at runtime. Fields with init=False, and the presence of ClassVar and InitVar is just two examples.
In terms of types of arguments passed to __init__(), they totally can be incompatible with the runtime type of the given field. There's nothing wrong with that. On the contrary, that is a useful feature. The obvious example is factories for field defaults. E.g. you can't store a list instance as a default value, it would be reused across your type's instances. Instead, you use default_factory=list. But when you do, what type does the respective argument to __init__ accept? Well, it's Union[YourType, MISSING] because you can omit it and it will initialize your type for you.
Type conversion is another common example. Your __init__ might accept timestamps as strings but always convert them to datetimes. Maybe you accept Unicode strings but internally store as bytes or vice versa? Maybe you support an external API so you have to accept a bunch of arguments that you later convert to a saner internal state? There are numerous use cases.
Summing up, don't confuse the signature of __init__ with the type shape.
Once you switch to this model, you can do convenient things like landing straight from the review system (by a "Ship it" button), landing after some checks were successfully performed, and so on.
Incidentally, this is similar to how merging pull requests by rebasing works on GitHub.
A bit of empathy would go a long way.
Good question about abstract base classes! Paraphrasing a well known cliché: types in functions should be forgiving in arguments (what the function accepts) and strict in return values (what the function emits). In our case, the human reviewer needs to decide if the argument types collected by MonkeyType should be generalized. In fact, the collected types might not even work in all cases and the type checker might complain. It's because annotations describe "what should be" whereas MonkeyType finds "what is". This is why a system like MonkeyType shouldn't even attempt to use abstract base classes in place of concrete types that it collected.
Python's type annotations are in fact very similar to Flow and Hack in the sense that they provide gradual typing. The specification (see: PEP 484) describes that only annotated functions are type checked. Calls to non-annotated code are treated as accepting any type in arguments and returning the Any type (a special type which effectively silences the type checker).
This generates a chicken and egg problem: if you don't have enough functions annotated, the type checker won't be able to provide meaningful output to you. So convincing people to annotate their code is harder: they don't see the benefit right away. Worse yet, you already have tens of thousands of functions in your code that you know work in production but were written before type annotations were introduced. It's not really feasible to come back and fill this information manually.
MonkeyType is a tool that gathers types at runtime and enables putting them back in your code as annotations. The goal is for mypy to have more information to work with, making it way more useful.
We knew we're going to open-source each others' implementations, esp. that Instagram's is focusing solely on Python 3 which isn't useful for Dropbox at the moment. It just took a while to get through the process of open sourcing what we had (cleaning up the early implementation with limited documentation, decoupling from internal data stores, etc.).
Would it be cheaper if this started out in the open? Probably, but I don't think by quite the margin as you expect.
PyAnnotate couples re-applying the types with the tool, and uses type comments for this.
MonkeyType generates .pyi files that you can either use directly or re-apply them to your code as proper type annotations.
Other than that, MonkeyType is used on a daily basis internally and solves a bunch of common annoyances of systems like these, like duplicates in unions, applying better types to stuff that was already hand-annotated with Any, etc.
It's fiction. Yes, inspired by a true story but fiction nonetheless.
You wouldn't base your knowledge about Nikola Tesla on "The Prestige", would you?
Rebooting helps. Feels like trying to use Windows on a cheap desktop to watch an HD movie in 2001.
Just one example: http://www.businessinsider.com/facebook-housing-menlo-park-2...
He did, it's called "The Bed of Procrustes". I quite enjoyed it, there's quite a few brilliant thoughts there, even though they're sprinkled between countless cheap shots against his enemies.
The batteries on those machines are amazing. They really last for 7-9 hours depending on use. The 2013 one is starting to show the "Service battery" warning every now and then but no performance degradation is noticeable yet. It's been through 380 cycles so far.
If you use a non-Retina Macbook like those, I suggest using a bitmap font for programming (I'm using Terminus myself). It's totally sharp and easy on the eyes. Crank up the contrast in your editor's color theme and you're all set.
This is pretty hilarious by the way because if the phone just about pairs and she turns the car off, iPhone keeps playing the music, just switching to the internal speaker. We have a running joke about her "walk-in theme playing".
The effect is faster shutdown with less "copy on read" but some __del__ methods and finally: blocks in some generators might never get called.