HN discussion of above: https://news.ycombinator.com/item?id=44107655
How Well Do New Python Type Checkers Conform? A Deep Dive into Ty, Pyrefly, and Zuban (2025-08-29) https://sinon.github.io/future-python-type-checkers/
def fn(x: str):
if x is None:
x = "123" # pyright flags that as unreachable code, pyrefly does not
Autocomplete for modules also doesn't work for me yet: import os
os. # I'll get `ABC, `Any`, `AnyStr`, `AnyStr_co`, `BinaryIO`, ...
Looking forward to have a fast language server for python though, pyright is way too slow for large projects.Looks like none of these new type checkers are ready yet.
There's no way to get an actual null reference, afaik. Variables always have some value, possibly None.
(not sure what happens if you set a reference to null from C - a crash, probably?)
I used to find this kind of tooling explosion exhausting (and back then with JS it truly was), but generally speaking it's a good sign that the community is hungry to push things forward. Choice is good, as long as we keep the Unix spirit alive: small, composable tools that play nicely together and can be interchanged.
The problem with the python tooling is no one can get it right. There aren't clear winners for a lot of the tooling.
There is a reason us old timers mostly wait on the sidelines until the dust settles.
We have seen this movie already too many times.
So which is it that you want, to just reach for one tool or have tools that have specific design goals and then have to reach for many tools in a full workflow?
FWIW Astral's long term goal appears to be to just reach for one tool, hence why you can now do "uv format".
Nowadays I'm finding myself using Zed a lot more, so maybe the story will be that all these nice Rust based tools become baked in giving it super powers for Python.
> pyrefly, ty, pyright, and basedpyright
All of them will complain 2-4x more about your code than PyCharm. I had more than 300 typing errors when I first opened my 20k LOC project in pyright that I wrote in Pycharm.
PyCharm works great when your code is not annotated. It infers types very well. But it won't complain in a lot of cases when your code is annotated.
Related reddit post https://old.reddit.com/r/Python/comments/1ajnikt/to_pycharm_...
They are designed for code which are more or less fully typed, as opposed to PyCharm which cobbles together a bunch of heuristics to try to make sense out of untyped code. An admirable quest, but not one I'm personally interested in.
And their insistence on only supporting this approach drove my entire team away from using PyCharm.
(From shallowly observing notifications on the 20+ typehint related issues I'm subscribed to, they seem to have kinda turned around and working toward fully supporting the python type system finally - possibly by integrating with one of the third-party type-checkers)
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.
Already there is some support in Pydantic for native dataclasses: https://docs.pydantic.dev/latest/concepts/dataclasses/
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 =)
They all lack certain features vs basedpyright (what I was using) such as auto imports (Ty has experimental support), showing signature/doc when selecting autocomplete options (I think Ty does have this one) and some other features that I can't remember.
One feature that always existed in Jedi (and now also Zuban) is "goto declaration" in Python. It allowed me to goto the "import" instead of the original definition of a function/class which I'm surprised either isn't supported at all (pyright?) or would just do the same thing as "goto definition" (Ty), seems like an obvious oversight imho.
Edit: Also, I wish all these new tools give more importance to such IDE features as much as they do for type checking.
New typecheckers don't need to be perfect. They need to be good enough, easy to integrate and have low false positives. Sure, they will improve with time, but if feels like a pain then no one will pick it up. Python users are famously averse to tools that slow down their dev cycles, even if it means better long term stability
BasedPyright is popular because it comes built-in with Cursor and disappears into the background. I have a positive bias towards Astral figuring it out given their track record. But, none of these tools have reached the point of effortlessness just yet.
You can take a look yourself if you think some of them are wrong: https://github.com/python/typeshed/tree/main/stdlib/asyncio
The advantage is type hints can be fixed without needing to release a new version of Python. The disadvantage is there's a lot of code in the standard library that doesn't really consider how to represent it with type hints, and it can be really tricky and not always possible.
I'm surprised to see so many people moving to pyrefly, ty, and zuban so quickly. I was going to wait until some time in 2026 to see which has matured to the point I find useful, I guess some users really find existing solutions actually unworkable.
Hmm. Presumably mypy and pyrefly use the same ones, but then I don't understand why pyrefly is complaining and mypy isn't:
ERROR Argument `Future[list[BaseException | Any]]` is not assignable to parameter `future` with type `Awaitable[tuple[Any]]` in function `asyncio.events.AbstractEventLoop.run_until_complete` [bad-argument-type]
--> xxx/xxx.py:513:33
|
513 | loop.run_until_complete(tasks.gather(*x, return_exceptions=True))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The definition in typeshed is this: def run_until_complete(self, future: _AwaitableLike[_T]) -> _T: ...
…where is it even puling "tuple[Any]" from…(tbh this is rather insignificant compared to the noise from external packages without type annotations, or with incomplete ones… pyrefly's inferences at the existence of attributes and their types are extremely conservative…)
> …where is it even puling "tuple[Any]" from…
Perhaps it's a bug in pyrefly, perhaps mypy or pyrefly is able to infer something about the types that the other isn't. I would strongly suggest checking their issues page, and if not seeing a report already report it yourself.
While there is an ongoing push to more consistently document the typing spec: https://typing.python.org/. It does not actually cover all the things you can infer from type hints, and different type hint checkers have decided to take different design choices compared to mypy and will produce different errors even in the most ideal situation.
This is one of the reasons why I am waiting for these libraries to mature a little more.
Indeed this is the cause of maybe 30% of the warnings I'm seeing… items being added to lists or dicts in some place (or something else making it infer a container type), and pyrefly then refusing other types getting added elsewhere. The most "egregious" one I saw was:
def something(x: list[str]) -> str:
foo = []
for i in x:
foo.append(i)
return "".join(foo)
Where it complains: ERROR Argument `str` is not assignable to parameter `object` with type `LiteralString` in function `list.append` [bad-argument-type]
--> test.py:4:20
4 | foo.append(i)
Edit: now that I have posted it, this might actually be a bug in the .join type annotation… or somethingEdit: nah, it's the loop (and the LiteralString variant of .join is just the first overload listed in the type hints)… https://github.com/facebook/pyrefly/issues/1107 - this is kinda important, I don't think I can use it before this is improved :/
foo: list[str] = []
If so this a type checking design choice: * What can I infer from an empty collection declaration?
* What do I allow further down the code to update inferences further up the code?
I don't know Pyrefly's philosophy here, but I assume it's guided by opinionated coding guidelines inside Meta, not what is perhaps the easiest for users to understand.
zelphirkalt•3h ago
f311a•3h ago
- zuban
- ty (from ruff team)
- pyrefly
One year ago, we had none of them, only slow options.
jerrygenser•3h ago
f311a•3h ago
wiseowise•32m ago
drcongo•3h ago
JimDabell•2h ago
https://github.com/microsoft/pyright/issues/1739
https://docs.python.org/3/glossary.html#term-EAFP
https://docs.python.org/3/glossary.html#term-LBYL
kstrauser•1h ago
maleldil•39m ago
JimDabell•18m ago
> I think EAFP is a very unfortunate and ill-advised practice.
They want you to not write the idiomatic Python:
…and instead write the non-idiomatic version: 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.jon-wood•2h ago
cruffle_duffle•1h ago
All I know is it is much more strict about stuff than pylance was.
Also a me problem!
wiseowise•29m ago
arccy•3h ago
vovavili•2h ago
insane_dreamer•2h ago
arccy•1h ago
wiseowise•24m ago
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.
tialaramex•2h ago
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.
f311a•2h ago
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.
tialaramex•2h ago
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.
kibwen•1h ago
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.