Type annotations are checked by a separate tool; the interpreter does not enforce them at runtime except where a library reads them (Pydantic, FastAPI, Typer).
| Checker | Notes |
|---|---|
| mypy | Reference implementation. Its plugin API is what django-stubs hooks into; pyright rejects plugins by design. |
| pyright | Written in TypeScript, distributed as an npm package with a PyPI wrapper. Implements the typing specification and powers Pylance in VS Code. |
| pyrefly | Meta’s checker, 1.0 since May 2026. PyTorch and JAX both carry its configuration in-tree; Meta runs it on Instagram. Strict defaults, designed for large codebases. |
| ty | Astral’s checker, in beta. Provides a gradual guarantee: adding annotations to working code does not introduce new errors. |
Useful constructs beyond basic parameter annotations:
| Construct | Use |
|---|---|
Literal["a", "b"] |
Closed sets of string or integer values; enables exhaustiveness checking. |
Protocol |
Structural typing. A class satisfies the protocol by shape, with no inheritance or import from the defining module. |
TypedDict |
Fixed-key dictionaries, for JSON structures where a model class is not wanted. |
Self, override |
Fluent APIs and explicit overrides (3.11 and 3.12). |
assert_never |
Compile-time exhaustiveness checks in match statements and if-chains. |
TypeAlias / type X = ... |
Named aliases for complex annotations. |
Generics syntax def f[T](...) |
Type parameters without TypeVar declarations (3.12+). |
[tool.pyright]
include = ["src", "tests"]
typeCheckingMode = "strict"
pythonVersion = "3.14"Existing untyped codebases are migrated module by module. The checker runs over the whole tree from the first commit, and the modules that do not pass are listed as overrides rather than excluded, so the exceptions stay visible and countable:
[tool.mypy]
strict = true
[[tool.mypy.overrides]]
module = ["legacy.*"]
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = ["vendorlib.*"]
ignore_missing_imports = truepyright expresses the same shape as a directory list: typeCheckingMode = "basic" across the repository, strict = ["src/newpackage"] for the part that has been converted.
Third-party packages without annotations fall into three cases: a stub package on PyPI (the types- distributions), a package that ships py.typed in a later version than the one pinned, and everything else, which gets ignore_missing_imports for that module or a hand-written stub under a stubs/ directory covering only the functions actually called. warn_unused_ignores and reportUnnecessaryTypeIgnoreComment report suppressions once they stop being needed, which is what keeps a migration from settling into a permanent list of ignores.
References
- The typing specification — what the checkers implement, and the conformance suite they are measured against.
- PEP 695 – Type Parameter Syntax — Final, 3.12.