Exhaustive Checks with Mypy

If you want to exhaustively check an enum (or a literal) and have Mypy tell you when you forgot a case, you can write yourself a tiny utility function:

from typing import NoReturn

def unreachable(v: NoReturn) -> NoReturn:
    raise AssertionError()

You can then use it with match (on Python 3.10) or is:


import enum

from .utils import unreachable

class Fruit(enum.Enum):
    APPLE = enum.auto()
    BANANA = enum.auto()

def using_match(fruit: Fruit) -> int:
    match fruit:
        case Fruit.APPLE:
            return 1
        case Fruit.BANANA:
            return 2
        case _:
            unreachable(fruit)

def using_is(fruit: Fruit) -> int:
    if fruit is Fruit.APPLE:
        return 1
    elif fruit is Fruit.BANANA:
        return 2
    else:
        unreachable(fruit)

But beware that this won't work for enums when using == as Mypy can't know whether the operator has been overridden:

def using_eq(fruit: Fruit) -> int:
    if fruit == Fruit.APPLE:
        return 1
    elif fruit == Fruit.BANANA:
        return 2
    else:
        # error: Argument 1 to "unreachable" has incompatible
        #   type "Fruit"; expected "NoReturn"  [arg-type]
        unreachable(fruit)
0
Subscribe to my newsletter

Read articles from Marcel Jackwerth directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Marcel Jackwerth
Marcel Jackwerth

Software Engineer @ Memfault, Ex-Fitbit, Ex-Pebble