mypy & pyright Setup
Static type checkers catch bugs before runtime. Configure mypy or pyright in pyproject.toml, run in CI alongside ruff 0.9+, and tighten strictness as coverage grows.
Recipe
# pyproject.toml
[tool.mypy]
python_version = "3.14"
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = false
[tool.pyright]
pythonVersion = "3.14"
typeCheckingMode = "basic"uv run mypy src/
uv run pyrightWhen to reach for this:
- New Python service bootstrap
- CI pipeline hardening
- Monorepo with mixed typed/untyped packages
- Comparing mypy vs pyright on edge cases
Working Example
# pyproject.toml excerpt for a src-layout app
[project]
name = "demo-app"
requires-python = ">=3.14"
[dependency-groups]
dev = ["mypy>=1.14", "pyright>=1.1.390", "ruff>=0.9"]
[tool.mypy]
python_version = "3.14"
packages = ["demo_app"]
namespace_packages = true
explicit_package_bases = true
warn_redundant_casts = true
warn_unused_ignores = true
pretty = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[tool.pyright]
include = ["src"]
exclude = ["**/__pycache__", "tests/fixtures"]
pythonVersion = "3.14"
typeCheckingMode = "standard"
reportMissingImports = true# .github/workflows/types.yml
name: types
on: [pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync --dev
- run: uv run mypy
- run: uv run pyrightWhat this demonstrates:
packages+explicit_package_basesfor src layout mypy discovery- Per-module overrides relax tests while app stays typed
- pyright
standardmode stricter thanbasic - CI runs both checkers when team wants maximum signal
Deep Dive
How It Works
- mypy - Incremental nominal checker; strong ecosystem plugins (django, pydantic).
- pyright - Fast; powers Pylance; good for large repos and strict inference.
- Stubs -
types-*packages on PyPI for untyped third-party libs. - py.typed - PEP 561 marker in installed packages exporting types.
- ruff - Linter/formatter; separate from type checking (though ruff has limited type-aware rules).
Strictness Progression
| Stage | mypy flags |
|---|---|
| 1 | warn_return_any |
| 2 | check_untyped_defs |
| 3 | disallow_untyped_defs per package |
| 4 | strict = true on new modules |
Python Notes
# install stubs for requests
uv add --dev types-requests
# mypy single file
uv run mypy src/demo_app/main.py --show-error-codesGotchas
- mypy cannot find imports - src layout without
packagesconfig. Fix:mypy_pathor package config + editable install. - pyright missing stubs - Reports unknown types. Fix: Install stubs or
reportMissingTypeStubs = falsetemporarily. - # type: ignore abuse - Hides real errors forever. Fix:
warn_unused_ignoresand periodic cleanup. - Checking venv site-packages - Slow and noisy. Fix: Scope
include/packagesto your code. - Divergent mypy/pyright results - Pick primary gate; use secondary as advisory until aligned.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| pyright only | VS Code/Pylance team | mypy-specific plugins needed |
| mypy only | Django/mypy plugins | Want Pylance parity |
| pytype | Google legacy stacks | Greenfield |
| no checker | Throwaway script | Production services |
FAQs
mypy or pyright?
Many teams run mypy in CI and pyright in IDE via Pylance - or pick one CI gate to start.
where config lives?
pyproject.toml [tool.mypy] and [tool.pyright] preferred over scattered ini files.
strict from day one?
Usually too noisy on brownfield - tighten module by module with overrides.
type ignore format?
# type: ignore[arg-type] specific codes - required in modern mypy with show_error_codes.
pydantic mypy plugin?
Pydantic 2 plugin improves model typing - enable in mypy config for FastAPI projects.
pre-commit hook?
- repo: local
hooks:
- id: mypy
name: mypy
entry: uv run mypy
language: system
types: [python]monorepo packages?
Separate mypy overrides per package path or use mypy -p package in CI matrix.
python_version pin?
Match requires-python and CI matrix - 3.14 for this cookbook.
ruff replace mypy?
No - ruff format/lint complements; type checking remains mypy/pyright job.
CI failure on warnings?
Start errors only; promote warnings to errors as debt burns down.
Related
- Gradual Typing Strategy - rollout plan
- Type Hints Basics - annotation intro
- mypy and pyright in CI - lint section sibling
- Virtual Environments Quickstart - uv dev deps
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.