Exhaustive Checks with Mypy

To achieve exhaustive type checking on enums or literals with Mypy validation when a case is forgotten, you can implement a utility function:

from typing import NoReturn

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

The utility works with Python 3.10’s match statement or traditional is comparisons:

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)

This won’t work for enums when using equality operators, since Mypy cannot determine if == 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)