Skip to content

How to configure mypy strict mode

This guide assumes a project managed by uv, with mypy installed as a development dependency.

mypy skips completely unannotated function bodies by default. Strict mode requires annotations and enables optional checks, including checking those bodies. It still permits some uses of Any; the exact flag bundle is defined by the installed mypy release.

Enable strict mode

Add this to pyproject.toml:

[tool.mypy]
strict = true

Run the checker:

uv run mypy src/

uv run executes the command inside the project’s virtual environment, ensuring all dependencies are installed first. A clean run prints Success: no issues found ... and exits 0; type errors exit 1.

For a one-off check, uv run mypy --strict src/ applies the same strict preset. Keep persistent policy in the configuration used by both CI and editors.

Inspect the strict checks

Run uv run mypy --help to see the installed release’s --strict expansion. Its core checks include:

Setting Check
disallow_untyped_defs / disallow_incomplete_defs Missing or incomplete function annotations
disallow_untyped_calls Calls from typed code to unannotated functions
check_untyped_defs Known operations inside unannotated function bodies
disallow_untyped_decorators Untyped decorators applied to typed functions
disallow_any_generics / disallow_subclassing_any Missing generic parameters and subclassing Any
warn_return_any / warn_redundant_casts / warn_unused_ignores Returning Any, redundant casts, and stale ignores
no_implicit_reexport / strict_equality / extra_checks Implicit exports, non-overlapping comparisons, and additional checks

This list describes the preset’s main checks. warn_unreachable and warn_unused_configs require separate settings. Strict bytes behavior is a default rather than an extra check supplied by strict.

A function’s unannotated parameters remain Any during body checking, so check_untyped_defs alone does not provide the same protection as annotated parameters.

Require annotations in selected modules

For an existing project, check bodies globally and enable individual checks in modules that are ready:

[tool.mypy]
check_untyped_defs = true

[[tool.mypy.overrides]]
module = ["mypackage.api", "mypackage.api.*"]
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_calls = true
warn_return_any = true

Include both a package’s name and its submodule pattern. These flags require annotations, reject untyped calls from typed code, and reject returning Any from typed functions. Keep strict = true in the global table: putting it inside an override can enable the strict bundle outside the selected modules. See the gradual adoption recipe.

For a project that is almost ready, start strict and disable individual settings deliberately:

[tool.mypy]
strict = true
warn_return_any = false

Handle dependencies without annotations

Missing-import diagnostics also occur outside strict mode. Install available stub packages before adding an exception:

uv add --dev types-requests types-PyYAML

uv records the stub packages as development dependencies and updates uv.lock. Check that the stub versions match the project’s runtime libraries.

If no usable annotations or stubs exist, suppress only the intended package:

[[tool.mypy.overrides]]
module = ["some_untyped_library", "some_untyped_library.*"]
ignore_missing_imports = true

Imported values may then become Any, reducing checks on calls into that dependency. Keep the exception narrow instead of setting ignore_missing_imports globally.

Keep suppression and CI behavior explicit

Use # type: ignore[error-code] on the line that mypy reports. With warn_unused_ignores enabled by strict mode, stale ignores become errors too. Options with warn_ names still produce failing mypy diagnostics; they are not advisory CI warnings.

Keep the same project configuration and lockfile in CI. Invalid command usage or a fatal checking problem can exit 2, distinct from the 1 used for ordinary type errors.

Last updated on