mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Adverse interaction between `unreachable` code, `type: ignore[...]`, and `pass` or `...`

Open finite-state-machine opened this issue 2 years ago • 1 comments

Bug Report

Both pass statements and the ellipsis (...) are treated as non-statements by mypy, leading to unexpected handling of an unreachable cases.

To Reproduce

Run the following with --strict --warn-unreachable:

mypy-play.net gist

def func1(value: int) -> None:
    if not isinstance(value, int):
        pass  # type: ignore[unreachable]       # error: [unused-ignore]
        print("there was a problem ...")        # error: [unreachable]

Changing pass to a literal ellipsis (...) also reproduces this problem.

(Motivation: since # type: ignore[...] directives must appear on the same line they affect, placing the # type directive on a pass statement can avoid very long lines.)

Expected Behavior

mypy should treat pass as the unreachable statement in this case.

Actual Behavior

mypy's output is:

main.py:3: error: Unused "type: ignore" comment  [unused-ignore]
main.py:4: error: Statement is unreachable  [unreachable]
Found 2 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.5.1
  • Mypy command-line flags: --strict --warn-unreachable
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.8, 3.11

finite-state-machine avatar Oct 06 '23 10:10 finite-state-machine

I also ran into this problem and wanted to share the use case leading me to this issue. Let's assume we have a Python module that contains pytest test cases. We want to skip all test cases in the module, so we use pytest.skip as follows:

import pytest

pytest.skip("Skip tests due to XYZ", allow_module_level=True)

def test_abc() -> None:
    ...

Mypy will report that the line after pytest.skip is unreachable. However, adding type: ignore[unreachable] simply converts the warning into unused-ignore, as reported by the OP.

As a workaround, I was able to add type: ignore[unreachable, unused-ignore]. This is obviously no big deal, but the behavior is a bit surprising.

seifertm avatar Dec 04 '25 14:12 seifertm