We've just uploaded mypy 2.4.0 to the Python Package Index (PyPI). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows:
python3 -m pip install -U mypy
You can read the full documentation for this release on Read the Docs.
Python 3.15 Support
Mypy 2.4 supports running on Python 3.15 and type checking most Python 3.15 features. This includes the new builtin sentinel type for sentinel values (PEP 661), which mypy now supports (see below for details). Lazy imports (PEP 810) and unpacking in comprehensions (PEP 798) are also supported. Closed TypedDicts (PEP 728) have been supported since mypy 2.2, but the extra_items TypedDict argument, also introduced in PEP 728, is still unsupported. Support for extra_items will be added in a future mypy release.
Native Parser Enabled by Default
Mypy now uses the new native parser by default. It's based on the Ruff parser, and it's significantly faster than the legacy parser, which uses the stdlib ast module. The native parser also has other benefits:
- You can target newer Python versions and use recent Python syntax even when running mypy on an older Python version. For example, you can use --python-version 3.15 when running mypy on Python 3.14.
- Stub files can use syntax that is newer than the target Python version. For example, stubs can use the PEP 695 generic class syntax (class Box[T]: ...) when running on or targeting Python 3.10.
- Parallel type checking requires the native parser.
The legacy parser is still available through --no-native-parser, or native_parser = False in the config file (native_parser = false under [tool.mypy] in pyproject.toml). We are planning to remove the legacy parser in early 2027. If you run into a problem with the native parser, please report it on the issue tracker.
Unlike the legacy parser, the native parser doesn't support type comments for variables defined by for and with statements. These type comments are silently ignored, and the types of the variables are inferred instead. This is a very rarely used feature that predates variable annotations that were introduced in Python 3.6. Other forms of type comments are still supported. Examples of type comments that are now ignored:
for i in x: # type: int
...
with foo() as a: # type: Foo
...
You can use variable annotations before the statements instead:
i: int
for i in x:
...
a: Foo
with foo() as a:
...
Related changes:
- Make the native parser the default (Ivan Levkivskyi, PR 21823)
- Fix native parser crash caused by invalid assignment expression (Jukka Lehtosalo, PR 21882)
- Fix native parser crashes on non-UTF-8 encoded source files (Ivan Levkivskyi, PR 22066)
- Match native parser behavior for positional-only arguments (Ivan Levkivskyi, PR 21891)
- Preserve docstrings in the native parser (Ivan Levkivskyi, PR 21890)
- Use the native parser for parse_type_string() (Ivan Levkivskyi, PR 21965)
- Fall back to sequential parsing when threads are unavailable (Jukka Lehtosalo, PR 21993)
- Various improvements to ast-serialize (Ivan Levkivskyi)
Parallel Checking No Longer Experimental
Parallel type checking is no longer experimental, and it will be enabled by default in a future mypy release. Use -n8 or --num-workers 8 to manually specify the number of workers.
Mypy now also supports automatic determination of worker count using -n auto, which selects the number of workers based on the CPU cores available to mypy. The auto mode is capped at 8, since very large numbers of workers can use a lot of memory if type checking large projects, but you can manually specify higher numbers.
You can also specify these in mypy.ini (e.g. num_workers = auto under [mypy]) or pyproject.toml (e.g. num_workers = "auto" under [tool.mypy]).
We've seen up to a 5x speedup when using 8 workers.
Refer to the documentation for more information.
Related changes:
- Use two-phase type checking in sequential mode (Ivan Levkivskyi, PR 21973)
- Use a more thorough cleanup of parallel workers (Ivan Levkivskyi, PR 21981)
- Increase riscv64 worker startup timeout (Michael R. Crusoe, PR 21988)
- Fix errors with multi-process workers on Windows (Chad Dombrova, PR 22016)
- Increase worker connection timeout on macOS (Ivan Levkivskyi, PR 22029)
- Try adding a simple retry on worker startup (Ivan Levkivskyi, PR 21980)
- Support automatic worker selection (Kevin Kannammalil, PR 21983)
Support for PEP 661 Sentinels
Mypy now supports sentinel values (PEP 661). Sentinels are useful for distinguishing a missing argument from valid values such as None. You can use a sentinel directly in a type annotation, and mypy narrows the type when you check its identity with is or is not.
# On Python 3.15, sentinel is a builtin and the import isn't needed
from typing_extensions import sentinel
MISSING = sentinel("MISSING")
def process(value: int | None | MISSING = MISSING) -> None:
if value is MISSING:
print("No value")
else:
reveal_type(value) # int | None
Related changes:
- Add support for PEP 661 sentinels (Edgar Ramírez Mondragón, PR 21647)
- Improve sentinel narrowing (Marc Mueller, PR 21844)
- Fix sentinel identity loss through generic substitution and inference (Edgar Ramírez Mondragón, PR 21888)
- Represent sentinels as first-class literal types (Ivan Levkivskyi, PR 21919)
- Recognize plain aliases of an existing sentinel as sentinel declarations (Edgar Ramírez Mondragón, PR 21972)
- Fix stubtest false positive for sentinel default values (Edgar Ramírez Mondragón, PR 22017)
More Lenient Handling of *tuple[Any, ...]
Mypy now treats *tuple[Any, ...] more leniently in tuple types and variadic generic types. The unpacked portion can match any number of items when checking compatibility, while explicitly specified items still need to match. Variadic tuples with a non-Any item type, such as *tuple[int, ...], are still handled strictly. This also improves type inference and support for indexing, slicing, and unpacking these tuples.
from typing import Any
def process(values: tuple[int, str]) -> None:
...
def example(values: tuple[int, *tuple[Any, ...]]) -> None:
process(values) # Now accepted
first, second = values # Now accepted
reveal_type(first) # int
reveal_type(second) # Any
Related changes:
- Lenient handling of *tuple[Any, ...] (part 1) (Ivan Levkivskyi, PR 22001)
- Lenient handling of *tuple[Any, ...] (part 2) (Ivan Levkivskyi, PR 22006)
- Lenient handling of *tuple[Any, ...] (part 3) (Ivan Levkivskyi, PR 22014)
Mypyc Generator and Coroutine Improvements
This release includes many improvements to the performance of generators and async functions. The improvements are particularly significant on free-threaded Python builds (over 2x faster in microbenchmarks compared to mypy 2.3).
Related changes:
- Keep generator local state in C locals when possible (Jukka Lehtosalo, PR 21982)
- Ensure a generator can't be entered while it's being executed (Jukka Lehtosalo, PR 21939)
- Use the generator helper fast path on resume (Jukka Lehtosalo, PR 21954)
- Move private generator spills from environments to generator objects (Jukka Lehtosalo, PR 21955)
- Keep all compiler spills on generator frames (Jukka Lehtosalo, PR 21958)
- Add the am_send slot and use PyIter_Send for await and yield from (Jukka Lehtosalo, PR 21979)
- Fix ownership of borrowed generator op result spills (Jukka Lehtosalo, PR 21986)
- Borrow generator frame state more aggressively (Jukka Lehtosalo, PR 21987)
- Avoid polluting generator environment layouts (Jukka Lehtosalo, PR 21990)
- Move non-captured generator state to private frames (Jukka Lehtosalo, PR 21991)
- Create function wrappers of nested coroutines lazily (Jukka Lehtosalo, PR 22048)
- Treat callable environment links as final (Jukka Lehtosalo, PR 21956)
- Borrow callable environment links (Jukka Lehtosalo, PR 21957)
Performance Improvements
- Reduce TypeForm recognition slowdown (David Foster, PR 21833)
- Try not simplifying unions during unification (Ivan Levkivskyi, PR 22035)
Mypyc Improvements
- Fix memory leak in Exception subclasses with instance attributes (Vaggelis Danias, PR 21719)
- Record MRO ancestor modules as dependencies of subclass-defining modules (Giorgos Michas, PR 21743)
- Fix inconsistent vtable method prefix (Piotr Sawicki, PR 21812)
- Fix function wrapper descriptor for static and class methods (Piotr Sawicki, PR 21811)
- Propagate property setter return value (Piotr Sawicki, PR 21814)
- Inline tuple operations (dnwpark, PR 21828)
- Clear only subtype references in tp_dealloc of built-in subtypes (Piotr Sawicki, PR 21872)
- Fix failed casts in non-native attribute setters (Jukka Lehtosalo, PR 21877)
- Fix librt.threading.Lock runtime type checks (Jukka Lehtosalo, PR 21876)
- Fix truthy-bool checks for fixed-width integers (Shantanu, PR 21900)
- Specialize bytearray(bytes_value[i:j]) (Shantanu, PR 21901)
- Fix error handling when unboxing tuples (Jukka Lehtosalo, PR 21952)
- Error on Unicode surrogates in mypyc (Ivan Levkivskyi, PR 21936)
- Fix name conflicts when creating CPyFunction variables (Piotr Sawicki, PR 21992)
- Synchronize native-to-native imports (Piotr Sawicki, PR 21887)
- Make native attribute writes faster with free threading (Jukka Lehtosalo, PR 21748)
- Fix missing modules during concurrent circular imports (Piotr Sawicki, PR 22027)
- Use a shared value for a Final attribute initialized in a class body (Jukka Lehtosalo, PR 21932)
Fixes to Crashes
- Fix crash in match statements with unpacked tuple subjects (NIYONSHUTI Emmanuel, PR 21989)
- Fix crash on invalid Concatenate usage (themylogin, PR 21562)
- Fix crashes involving diverging recursive protocols (Ivan Levkivskyi, PRs 22036 and 22053)
Documentation Updates
- Update docs for modern setuptools (Shantanu, PR 21792)
- Document the lack of abi3 support in mypyc (Michael R. Crusoe, PR 22033)
Packaging Changes
- No longer provide mypyc-accelerated wheels for macOS x86_64 mypyc-wheels PR 119
Other Notable Fixes and Improvements
- Add basic support for PEP 798 (Ivan Levkivskyi, PR 21976)
- Generalise PEP 798 support to SupportsKeysAndGetItem (Shantanu, PR 22021)
- Improve stubtest handling of expected dunder methods (Alex Waygood, PR 21764)
- Fix another variance issue with __replace__ (Shantanu, PR 21737)
- Avoid false unused-awaitable errors for task groups (A5rocks, PR 21766)
- Fix strict-optional handling of generic defaults (Shantanu, PR 21788)
- Allow ParamSpec in NamedTuple and TypedDict (A5rocks, PR 21044)
- Replace builtins.ellipsis with types.EllipsisType (Ali Hamdan, PR 21911)
- Support in and not in tests on sys.platform (Jelle Zijlstra, PR 21913)
- Fix match statement narrowing for self-matching class patterns with literal arguments (NIYONSHUTI Emmanuel, PR 21918)
- Fix callable enum narrowing (NIYONSHUTI Emmanuel, PR 21946)
- Handle Self wrapped in Annotated when checking the expected self type (NIYONSHUTI Emmanuel, PR 21928)
- Don't error unnecessarily about new syntax in dependencies where errors are ignored (A5rocks, PR 21883)
- Handle edge case in overload subtyping (Ivan Levkivskyi, PRs 22007 and 22030)
- Unify type alias deferral logic (Ivan Levkivskyi, PR 22015)
- Narrow match-pattern AST annotations to concrete variants (Adam Dangoor, PR 22000)
- Narrow built-in signature-hook return types (Adam Dangoor, PR 21995)
Typeshed Updates
Please see git log for the full list of standard library typeshed stub changes.
Acknowledgements
Thanks to all mypy contributors who contributed to this release:
- A5rocks
- Adam Dangoor
- Alex Waygood
- Ali Hamdan
- Brian Helba
- Chad Dombrova
- David Foster
- dnwpark
- Edgar Ramírez Mondragón
- Giorgos Michas
- Ivan Levkivskyi
- Jelle Zijlstra
- Jukka Lehtosalo
- Marc Mueller
- Michael R. Crusoe
- NIYONSHUTI Emmanuel
- Piotr Sawicki
- Shantanu
- themylogin
- Vaggelis Danias
- Willy Bruns
I'd also like to thank my employer, Dropbox, for supporting mypy development.