Skip to content
← All Python articles

Python explainer

What mypy Strict Mode Turns On

About 5 min to read. Allow extra time to work through examples.

Learn the strict-mode errors you will meet most often, what each error code means, and how to fix it. The full list of strict flags is at the end.

This article uses Python. You can make it your practice mode to browse related content across the site.

In the last exercise, mypy rejected code that passed its visible tests. Here are two more errors you will see often:

import json


def load_port(text: str) -> int:
    config = json.loads(text)
    return config["port"]


def count(items: list) -> int:
    return len(items)
L6:5 [no-any-return]
Returning Any from function declared to return "int"

L9:18 [type-arg]
Missing type arguments for generic type "list"

Plain mypy, without --strict, accepts this file. Both errors come from strict mode.

Strict mode is a list of checks

--strict turns on thirteen flags at once. Each flag adds one check, and the error code in square brackets tells you which check found the error. Other errors, such as [union-attr] from the last exercise, come from mypy's default checks. You see those with or without --strict.

Here are the strict-mode errors you will meet most, with the usual fix for each.

Annotate every function

[no-untyped-def] means a function has no annotation for a parameter or for its return value. A function that returns nothing still needs -> None:

def log(message: str) -> None:
    print(message)

[no-untyped-call] means annotated code calls an untyped function, one with no annotations. Annotate the function you call, and the error goes away.

Say what a collection holds

[type-arg] means you wrote list, dict or set without saying what it holds. Write list[str] or dict[str, int] instead. A bare list can hold anything, so mypy cannot check what you take out of it.

Check an Any value before you return it

Any is the type mypy gives a value it knows nothing about. mypy allows every operation on an Any value and checks none of them. json.loads returns Any, because mypy cannot know what the JSON text holds.

[no-any-return] means a function that promises a real type returns an Any value instead. Check the value first. After the isinstance check, mypy knows that port is an int:

import json


def load_port(text: str) -> int:
    config = json.loads(text)
    port = config["port"]
    if not isinstance(port, int):
        raise ValueError("port must be an integer")
    return port

Compare values that can be equal

[comparison-overlap] means you compared two values that can never be equal:

def is_owner(owner_id: int, user_id: str) -> bool:
    return owner_id == user_id  # always False

Python allows the comparison and returns False every time. Convert one side first, for example with int(user_id). Do it where the value enters your program.

Delete ignore comments you no longer need

A comment such as # type: ignore[arg-type] hides one kind of error on its line. If the line has no such error, strict mode reports [unused-ignore]. Delete the comment.

On this site, a # type: ignore with no error code in square brackets fails the submission, and so does a # mypy: comment. Either one can turn off mypy for the whole file. Fix the error instead.

Check your understanding mypy reports [no-any-return] on a function that returns a value from json.loads. Why fix it with a check instead of an ignore comment? Show answer

The comment hides the error, but the value can still be the wrong type. If the JSON holds the string "8080", the function returns it while promising an int, and the bug shows up later, far from its cause. An isinstance check fails at once, with a clear message, and tells mypy the real type.

What the site adds to strict mode

The site runs mypy with three more options:

mypy --strict --warn-unreachable --extra-checks --ignore-missing-imports
  • --warn-unreachable reports code that can never run, as [unreachable]. A common cause is if value is None: on a parameter typed str.
  • --extra-checks is already part of --strict.
  • --ignore-missing-imports lets you import a package that has no type information. mypy treats the package as Any.

The site also passes an empty --config-file=, so mypy ignores the settings in your own configuration files. Every learner's code gets the same check.

Going further: all thirteen strict flags

This is the list in mypy 2.4.0. It can change between releases, so run python -m mypy --help to see the list for the version you have.

Flag What it reports Error code
--disallow-untyped-defs A function with missing annotations no-untyped-def
--disallow-incomplete-defs A function with only some annotations no-untyped-def
--check-untyped-defs Errors inside untyped functions varies
--disallow-untyped-calls Typed code that calls an untyped function no-untyped-call
--disallow-untyped-decorators An untyped decorator on a typed function untyped-decorator
--disallow-subclassing-any A class whose base class is Any misc
--disallow-any-generics A generic such as list with no type arguments type-arg
--warn-redundant-casts A cast to the type the value already has redundant-cast
--warn-unused-ignores A # type: ignore that hides nothing unused-ignore
--warn-return-any Returning Any where a real type is promised no-any-return
--no-implicit-reexport Importing a name that a module did not re-export attr-defined
--strict-equality ==, != or in on types that can never match comparison-overlap
--extra-checks A few rarer checks, such as on TypedDict updates varies

Read more in mypy's command-line options and its list of error codes.

step 3 of 13

Getting Started with Tests and Type Checks

Up next Find the Redundant Flags in a mypy Command