Posted by brianzelip 10/14/2025
It has some things it can’t pick up on - no return after a catch block when all code paths are covered for example - but it’s speedy and the error messages are good enough for Claude to understand and ignore or resolve.
Recommended!
I've been leaning on pyright + django-stubs, but wondering if I'm missing something better with fewer gaps and pain points.
We've seen a lot of people have success with the mypy plugin + django-stubs.
Full out-of-the-box support is being actively worked on in Pyrefly: we will have specialized django enum support in the next release and we expect real experimental support by the end of the year. At that time we'll likely post a blog post to announce it [here](https://pyrefly.org/blog/).
- zuban
- ty (from ruff team)
- pyrefly
One year ago, we had none of them, only slow options.
It is interesting that nobody was writing these tools in C or in C++. There are obvious ergonomic reasons, but perhaps also it matters that Rust cares a lot more about types than either of those languages.
This one's easier to explain. People interested in tooling for a specific language probably want to write that tooling in that language (hence pip, poetry, mypy, jedi, etc). Normally that would be the end if it, if Python wasn't 10-100x slower than a natively compiled language. And going from Python to Rust is an order of magnitude easier than going from Python to C or Python to C++, because the compiler is so good at identifying silly mistakes. Rust is just a friendlier language.
The author of Zuban started writing it back in 2020 or 2021, so it took him more than 4 years to complete it. And he is the author of Jedi, so he had prior experience already.
Zuban seems to have a bunch of scary "I'm not sure if this is correct" unsafe blocks, which to me would be a red flag. I mean, it's better that there's a comment expressing the doubt, but my experience is that if you're not sure whether it's correct, it's probably not correct.
For anyone reaching for unsafe, there are in many cases either an existing API (split_at_mut comes to mind). For others, using zero-copy or bytemuck instead of unsafe is a good idea too.
None of that is to say "never write unsafe", unsafe existing is pretty much one of the reasons for Rust to be :)
For example in crates/zuban_python/src/file/diagnostics.rs:
"TODO this unsafe feels very wrong, because a bit lower we might modify the complex/ points."
or crates/zuban_python/src/database.rs:
"Points are guarded by specific logic and if they are overwritten by something that shouldn't it should not be that tragic."
I saw nothing where I was like "ZOMG this is definitely busted" but I definitely did not get the robust "Oh, I see now why this is correct" that I like from a good unsafe rationale comment, and these aren't tiny things like the small unsafe bit twiddling transmutes which are probably either actually correct or in any case will do what you expected at compile time and so any surprises are priced in without a rationale text.
https://github.com/microsoft/pyright/issues/1739
> I think EAFP is a very unfortunate and ill-advised practice.
They want you to not write the idiomatic Python:
try:
foo = bar["baz"]["qux"]
...
except KeyError:
...
…and instead write the non-idiomatic version: if "baz" in bar and "qux" in bar["baz"]:
foo = bar["baz"]["qux"]
...
else:
...
If this were a linter then I would accept that it is going to be opinionated. But this is not a linter, it’s a type checker. Their opinions about EAFP are irrelevant. That’s idiomatic Python.They don't always choose the same options, so some Python code may type check in one type checker and not in another.
Yes this is a dumb situation but that's how it is. So Pyright has to make a choice here, and they chose the most sensible option.
You're free to disagree of course.
All I know is it is much more strict about stuff than pylance was.
Also a me problem!
All of them had some big issue that prevented it from getting mainstream. Either it was slow, or didn’t work with existing workflow, or had complex configuration, or something that prevented gradual adoption.
uv is universally praised as the second coming Christ in Python world (and for a good reason). So no, I doubt there will be something else. Not only you need to be better than uv, you also need to have community momentum.
Pydantic is probably the problem here, but it is what it is.
foo = eval(result)
It can’t know what you’re going to load until it actually does it.Things which lean heavily into metaprogramming, typically ORMs or things like Pydantic, fall into that category. I can’t hold that against the type system.
I think we should. Dataclasses have existed in Python for an extremely long time, and yet the type system doesn't support defining your own similar classes. Kwargs have also existed forever, but they forgot to support that and had to add TypedDict's much later. And it still doesn't properly support optional fields. There's a lot of stuff like this in the language which are unbelievably frustrating, because for some reason they implemented the syntax before implementing a typechecker. Everything has been hacked in ever since. I consider python's type system to be a lost cause, just hoping for someone to make the Typescript equivalent for Python.
I don't think you understand what Pydantic brings to the table or why people use it. It has lots more to do with serialization, complex validation and data mapping.
Already there is some support in Pydantic for native dataclasses: https://docs.pydantic.dev/latest/concepts/dataclasses/
[1]: https://docs.python.org/3/library/typing.html#typing.datacla...
[1]: https://docs.python.org/3/library/dataclasses.html#dataclass...
[2]: https://docs.python.org/3/library/dataclasses.html#dataclass...
What I'm trying to point out is that these features exist in core Python and yet the type system they built can't express it. By contrast, TypeScript is designed in such a way that you can implement everything yourself without having to write "custom typescript checker plugins".
Aside from basic inheritance and complex nested types, the pydantic ‘TypeAdapter’ is awesome for simply validating native dataclasses. It’s a little slow, but everything pydantic is =)