[HN Gopher] Comparing Python Type Checkers: Typing Spec Conformance
       ___________________________________________________________________
        
       Comparing Python Type Checkers: Typing Spec Conformance
        
       Author : ocamoss
       Score  : 84 points
       Date   : 2026-03-16 12:25 UTC (10 hours ago)
        
 (HTM) web link (pyrefly.org)
 (TXT) w3m dump (pyrefly.org)
        
       | persedes wrote:
       | Article is a nice write up of
       | https://htmlpreview.github.io/?https://github.com/python/typ...
       | 
       | (glad they include ty now)
        
       | Pay08 wrote:
       | I still can't get over the utter idiocy in Python's type hints
       | being decorative. In what world does x: int = "thing" not give
       | someone in the standardisation process pause?
        
         | dcreager wrote:
         | Can you elaborate what you mean by decorative?
         | 
         | If you run a type checker like ty or pyright they're not
         | decorative -- you'll get clear diagnostics for that particular
         | example [1], and any other type errors you might have. You can
         | set up CI so that e.g. blocks PRs from being merged, just like
         | any other test failure.
         | 
         | If you mean types not being checked at runtime, the consensus
         | is that most users don't want to pay the cost of the checks
         | every time the program is run. It's more cost-effective to do
         | those checks at development/test/CI time using a type checker,
         | as described above. But if you _do_ want that, you can opt in
         | to that using something like beartype [2].
         | 
         | [1] https://play.ty.dev/905db656-e271-4a3a-b27d-18a4dd45f5da
         | 
         | [2] https://github.com/beartype/beartype/
        
           | Pay08 wrote:
           | Exactly my point. If I need to run 300 external tools for a
           | language feature to be worth a damn, why is it a language
           | feature?
        
             | wiseowise wrote:
             | 'uvx ty check' or 'uvx pyrefly check'. That's hardly 300
             | external tools.
        
         | badlibrarian wrote:
         | It's a community that delayed progress for a decade while they
         | waited for everyone to put parenthesis on the print statement.
         | Give 'em enough time and they'll figure out best practices.
        
         | Spivak wrote:
         | In C-ish languages the statement                   int x =
         | "thing"
         | 
         | is perfectly valid. It means reserve a spot for a 32 bit int
         | and then shove the pointer to the string "thing" at the address
         | of x. It will do the wrong thing and also overflow memory but
         | you could generate code for it. The type checker is what stops
         | you. It's the same in Python, if you make type checking a build
         | breaker then the annotations mean something. Types aren't
         | checked at runtime but C doesn't check them either.
        
           | lefra wrote:
           | In C, int may be as small as 16 bits You may get 32 bits (or
           | more) but it's not guaranteed. I don't see how you get a
           | memory overflow though?
           | 
           | I'd be surprised if a compiler with -Wall -Werror accepts to
           | compile this.
           | 
           | Trying to cast back the int to a char* might work if the
           | pointers are the same size as int on the target platform, but
           | it's actually Undefined Behaviour IIRC.
        
             | Pay08 wrote:
             | I guess an overflow would be possible if the size of a
             | point and int differs.
        
           | Pay08 wrote:
           | It's valid in C, due to semantics around pointers. Try that
           | in Java and you'll quickly find that it's not valid in "C-ish
           | languages". C absolutely checks types, it's just weakly
           | typed. Python doesn't check types at all, which I wouldn't
           | have a problem with, if the language didn't have type
           | annotations that sure look like they'll do something.
        
           | tialaramex wrote:
           | It won't "overflow memory".
           | 
           | This says there will be an immutable array of six bytes, with
           | the ASCII letters for "thing" in the first five and then the
           | sixth is zero, this array can be coerced to the pointer type
           | char* (a pointer to bytes) and then (though a modern C
           | compiler will tell you this is a terrible idea) coerced to
           | the signed integer type int.
           | 
           | The six byte array will end up in the "read only data"
           | section of the executable, it doesn't "overflow memory" and
           | isn't stored in the x. Even if you gave x a more sensible
           | type "char*" that word "thing" isn't somehow stored in your
           | variable, it's a pointer.
           | 
           | So, this isn't the same at all _and_ you don 't understand C
           | as well as you thought you did.
           | 
           | Edited: fix escaping bold markers
        
         | Daishiman wrote:
         | It's the complete opposite. The objective of type hints is that
         | they're optional precisely because type hints narrow the
         | functionality of the language. And evidenced by the fact that
         | different type checks have different heuristics for determining
         | what is a valid typed program and what isn't, it seems that the
         | decision is correct.
         | 
         | No type system will allow for the dynamism that Python
         | supports. It's not a question of how you annotate types, it's
         | about how you resolve types.
        
           | hrmtst93837 wrote:
           | Optional on paper, sure. Once you publish shared libs or keep
           | a nontrivial repo usable across teams, type hints stop
           | feeling optional fast, because the minute mypy, pyright, and
           | Pyre disagree on metaprogramming or runtime patching you get
           | three incompatible stories about the same program and a pile
           | of contraditions instead of signal. Python can stay dynamic,
           | yet this setup mostly buys busywork for CI and false
           | confidence for humans.
        
           | Pay08 wrote:
           | Nobody is saying they are mandatory, and I'm actually a big
           | fan of gradual typing. My point is that they do nothing.
           | 
           | However, type hints reducing the functionality of the
           | language isn't true either.
        
       | martinky24 wrote:
       | I've been using ty on some previously untyped codebases at work.
       | It does a good job of being fast and easy to use while catching
       | many issues without being overly draconian.
       | 
       | My teammates who were writing untyped Python previously don't
       | seem to mind it. It's a good addition to the ecosystem!
        
         | tfrancisl wrote:
         | And it makes it infinitely easier for them to get with the
         | times and start typing their code!
        
       | ddxv wrote:
       | I've used mypy forever and never even tried these others. Looking
       | at them though it looks like it's worth trying out Zuban or
       | Pyright? Is there a noticeable benefit when switching between
       | different checkers?
        
         | x187463 wrote:
         | Speed, especially in larger codebases.
        
         | rirze wrote:
         | If you care about correctness, unless you pick pyright, don't
         | bother at the moment. If you're creating a new project and
         | looking for a promise for better faster typing, then pick one
         | of Zuban, Pyrefly, or ty.
        
         | winrid wrote:
         | Mypy still best for Django
        
           | lastofus wrote:
           | As a long time Django user that wants to start using typing,
           | can you elaborate on why mypy is still the way to go?
        
             | vesselapi wrote:
             | Django's ORM generates a lot of dynamic attributes --
             | querysets, related managers, model fields -- that don't
             | exist as static class members. The django-stubs plugin for
             | mypy has years of work encoding those patterns as special-
             | case type narrowing rules. Pyright handles some of it
             | through its own django plugin, but mypy+django-stubs is
             | still the most battle-tested combo for catching things like
             | filter() field name typos or wrong annotation on a
             | ForeignKey. It's less about mypy being better in general
             | and more about the plugin ecosystem being further along.
        
       | Scene_Cast2 wrote:
       | Are there any good static (i.e. not runtime) type checkers for
       | arrays and tensors? E.g. "16x64x256 fp16" in numpy, pytorch, jax,
       | cupy, or whatever framework. Would be pretty useful for ML work.
        
         | ocamoss wrote:
         | We're working on statically checking Jaxtyping annotations in
         | Pyrefly, but it's incomplete and not ready to use yet :)
        
         | jmalicki wrote:
         | Have you looked at jaxtyping? I've found it to be pretty useful
         | - it works for PyTorch not just Jax.
         | 
         | https://github.com/patrick-kidger/jaxtyping
        
         | westurner wrote:
         | - /?hnlog pycontract icontract
         | https://westurner.github.io/hnlog/ :
         | 
         | From https://news.ycombinator.com/item?id=14246095 (2017) :
         | 
         | > _PyContracts supports runtime type-checking and value
         | constraints /assertions (as @contract decorators, annotations,
         | and docstrings)._
         | 
         | > _Unfortunately, there 's yet no unifying syntax between
         | PyContracts and the newer python type annotations which MyPy
         | checks at compile-type._
         | 
         | Or beartype.
         | 
         | Pycontracts has: https://andreacensi.github.io/contracts/ :
         | @contract       def my_function(a : 'int,>0', b :
         | 'list[N],N>0') -> 'list[N]':
         | @contract(image='array[HxWx3](uint8),H>10,W>10')       def
         | recolor(image):
         | 
         | For icontract, there's icontract-hyothesis.
         | 
         | parquery/icontract: https://github.com/Parquery/icontract :
         | 
         | > _There exist a couple of contract libraries. However, at the
         | time of this writing (September 2018), they all required the
         | programmer either to learn a new syntax (PyContracts) or to
         | write redundant condition descriptions ( e.g., contracts,
         | covenant, deal, dpcontracts, pyadbc and pcd)._
         | @icontract.require(lambda x: x > 3, "x must not be small")
         | def some_func(x: int, y: int = 5) -> None:
         | 
         | icontract with numpy array types:
         | @icontract.require(lambda arr: isinstance(arr, np.ndarray))
         | @icontract.require(lambda arr: arr.shape == (3, 3))
         | @icontract.require(lambda arr: np.all(arr >= 0), "All elements
         | must be non-negative")       def process_matrix(arr:
         | np.ndarray):           return np.sum(arr)
         | invalid_matrix = np.array([[1, -2, 3], [4, 5, 6], [7, 8, 9]])
         | process_matrix(invalid_matrix)       # Raises
         | icontract.ViolationError
        
       | extr wrote:
       | Wow, quite surprising results. I have been working on a personal
       | project with the astral stack (uv, ruff, ty) that's using
       | extremely strict lint/type checking settings, you could call it
       | an experiment in setting up a python codebase to work well with
       | AI. I was not aware that ty's gaps were significant. I just tried
       | with zuban + pyright. Both catch a half dozen issues that ty is
       | ignoring. Zuban has one FP and one FN, pyright is 100% correct.
       | 
       | Looks like I will be converting to pyright. No disrespect to the
       | astral team, I think they have been pretty careful to note that
       | ty is still in early days. I'm sure I will return to it at some
       | point - uv and ruff are excellent.
        
         | roflcopter69 wrote:
         | This is the way. For now, pyright it's also 100% pyright for
         | me. I can recommend turning on reportMatchNotExhaustive if
         | you're into Python's match statements but would love the
         | exhaustiveness check you get in Rust. Eric Traut has done a
         | marvellous job working on pyright, what a legend!
         | 
         | But don't get me wrong, I made an entry in my calendar to
         | remind me of checking out ty in half a year. I'm quite
         | optimistic they will get there.
        
       | pgwalsh wrote:
       | Using VSCodium I was having issues with python type checkers for
       | quite a while. I did the basedpyright thing for a while but that
       | was painful. It's a bit too based for me, and I'm not sure i'd
       | call it based. Right now I have uv, ruff, and ty and I'm happy
       | with it. It's super easy to update and super fast. I didn't
       | realize the coverage wasn't as good as some others but I still
       | like it. I may have to try pyrefly. Never heard of it until this
       | post, so thank you.
        
       | IshKebab wrote:
       | Interesting. This is the first I've heard of Zuban.
       | 
       | The fact that Mypy fails so badly matches my experience. It would
       | be interesting to see exactly where Pyright "fails". It's been so
       | reliable to me I wouldn't be 100% surprised if these are
       | deliberate deviations from the spec, where it is dumb.
        
       ___________________________________________________________________
       (page generated 2026-03-16 23:01 UTC)