Python Type Hints - How to Manage “type: ignore” Comments with Mypy

2021-05-25 Clamp down on them comments!

It seems inevitable that large projects need some # type: ignore comments, to work around type checking in tricky cases. I’ve found Mypy has a couple of opt-in flags to make such ignore comments more precise and manageable.

These flags are:

We can activate these flags for a whole project in setup.cfg like so:

[mypy]
show_error_codes = True
warn_unused_ignores = True

Let’s look at each flag in more detail.

show_error_codes

This first flag helps us write focused ignore comments that only disable the checks we want to ignore. When show_error_codes is enabled, Mypy identifies errors not only with messages but also codes. We can use these codes in ignore comments, reducing the risk of other errors being introduced on such lines.

Take this example code:

x: int
x: str

Mypy logs an error when we redefine the type of a variable like this. This is normally a reason to change our code, but let’s roll with it for this example.

Let’s run Mypy on this example, with show_error_codes = True:

$ mypy example.py
example.py:2: error: Name 'x' already defined on line 1  [no-redef]
Found 1 error in 1 file (checked 1 source file)

The error message is followed by the error code in square brackets: [no-redef]. We can use this bracketed error code in an ignore comment to silence only that error:

x: int
x: str  # type: ignore [no-redef]

Running Mypy now shows no errors:

$ mypy --show-error-codes example.py
Success: no issues found in 1 source file

Great.

We can introduce a different error on the ignored line and Mypy will still detect it. For example, we can add a reference to an undefined variable y:

x: int
x: str = y  # type: ignore [no-redef]

Mypy finds the bug:

$ mypy example.py
example.py:2: error: Name 'y' is not defined  [name-defined]
Found 1 error in 1 file (checked 1 source file)

This error would be ignore if the line used a generic ignore comment, without the bracketed error code.

If we ever need it, we can ignore two errors by combining their codes in a comma-separated list:

x: int
x: str = y  # type: ignore [name-defined,no-redef]

But this may also be a signal to split the line, or fix the causes of the errors!

warn_unused_ignores

This second flag helps us manage ignore comments as our code changes. When warn_unused_ignores is enabled, Mypy will log an error (not a warning) for each unnecessary ignore comment. Such redundancy can appear as our code evolves, such as when our type hints become more accurate.

For example, imagine if we changed our previous example to remove the first line:

x: str  # type: ignore [no-redef]

Now x is only defined once. Running Mypy with warn_unused_ignores = True:

$ mypy --warn-unused-ignores example.py
example.py:1: error: unused 'type: ignore' comment
Found 1 error in 1 file (checked 1 source file)

We get an error telling us that we can remove that ignore comment. Neat!

Fin

May your ignorance be ever reduced,

—Adam


🦄 Working on a Django project? Check out my book Speed Up Your Django Tests.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: mypy, python