# mypy 2.0 picks parallelism over a rewrite


For most of the past year, the speed conversation around Python type checkers has had one shape: [mypy](https://pydevtools.com/handbook/reference/mypy.md) is the slow incumbent, and the fast alternatives ([ty](https://pydevtools.com/handbook/reference/ty.md), [Pyrefly](https://pydevtools.com/handbook/reference/pyrefly.md), [Zuban](https://pydevtools.com/handbook/reference/zuban.md)) earn their speed by being written in Rust. [mypy 2.0](https://mypy-lang.blogspot.com/2026/05/mypy-20-relased.html), released May 6, 2026, takes a different route. It runs across multiple cores.

## Run mypy across cores

The headline feature is `--num-workers N` (or `-nN`). Pass a worker count and mypy splits type checking across processes:

```bash
uv run mypy -n 8 src/
```

The release notes report **up to 5x faster checking with 8 workers**, with the disclaimer that "the speedup depends on the import structure of your codebase and your environment." Parallel mode is marked experimental and implicitly enables mypy's [native parser](https://github.com/python/mypy/pull/21387) (the Ruff-based parser introduced in 1.18). The release notes acknowledge "minor semantic differences between parallel and non-parallel modes" that future releases will close, so single-process mypy remains the conservative choice for CI gates that cannot tolerate flake.

The work has been in flight for years. [Issue #933](https://github.com/python/mypy/issues/933) ("Faster, parallel type checking") has been open since 2015. The implementation that landed in 2.0 came together across [#21119](https://github.com/python/mypy/pull/21119), [#21324](https://github.com/python/mypy/pull/21324), and [#21387](https://github.com/python/mypy/pull/21387) earlier this year.

## Account for the breaking defaults

Three flag flips matter on upgrade because they can produce errors in code that was passing under mypy 1.x:

- `--local-partial-types` is now default. This changes inference for variables that the same name is assigned to in different scopes.
- `--strict-bytes` is now default per [PEP 688](https://peps.python.org/pep-0688/). Passing a `bytearray` or `memoryview` where `bytes` is expected no longer type-checks.
- `--allow-redefinition` now behaves like the prior `--allow-redefinition-new`, requires `--local-partial-types` (which is fine, since that is now default), and forbids two type annotations on the same variable. Pass `--allow-redefinition-old` to keep the legacy behavior.

The release also rejects `--python-version 3.9` outright. Targeting Python 3.9 was already deprecated; 2.0 promotes that deprecation to a hard error. The minimum target is now 3.10. mypy 1.20 had separately dropped runtime support for Python 3.9 itself.

A smaller cleanup worth knowing: `--ignore-missing-imports` is now respected uniformly. Previous versions special-cased a small set of bundled stubs, which produced surprising behavior when those packages got real stubs elsewhere.

## Read the speed gap honestly

Parallel checking does not turn mypy into ty. The handbook's [type checker comparison](https://pydevtools.com/handbook/explanation/how-do-mypy-pyright-and-ty-compare.md) benchmarks single-threaded mypy 1.20 at 0.88s on Rich and 2.39s on SQLGlot. Even at the 5x best case, parallel mypy on those projects still trails the Rust-based checkers on small codebases, where worker startup overhead eats much of the savings. The release explicitly frames the 5x figure for *large* projects.

What changes is the framing of the trade-off. The "Rust is the only path to fast Python type checking" claim is no longer the only argument worth taking seriously. mypy still has the broadest stub coverage and the most mature plugin ecosystem (Django, Pydantic, and SQLAlchemy all ship mypy plugins). For projects that already depend on those things, multi-core mypy may be enough to defer a migration.

For teams that have already moved or are mid-migration ([FastAPI's incremental adoption of ty](https://pydevtools.com/blog/migrating-from-mypy-to-ty-lessons-from-fastapi.md) is a useful template), 2.0 is mostly an upgrade-and-test exercise. Read the breaking-defaults list, run `uv run mypy -n 8 src/` on a clean checkout, and see what falls out.

## Learn more

- [mypy 2.0 release blog post](https://mypy-lang.blogspot.com/2026/05/mypy-20-relased.html)
- [mypy changelog](https://mypy.readthedocs.io/en/stable/changelog.html)
- [mypy reference](https://pydevtools.com/handbook/reference/mypy.md)
- [How do Python type checkers compare?](https://pydevtools.com/handbook/explanation/how-do-mypy-pyright-and-ty-compare.md)
- [How to configure mypy strict mode](https://pydevtools.com/handbook/how-to/how-to-configure-mypy-strict-mode.md)
- [How to migrate from mypy to ty](https://pydevtools.com/handbook/how-to/how-to-migrate-from-mypy-to-ty.md)
