Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds docs about exhaustive literal and enum checks #10860

Merged
merged 3 commits into from
Jul 23, 2021
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions docs/source/literal_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,73 @@ using ``isinstance()``:
This feature is sometimes called "sum types" or "discriminated union types"
in other programming languages.

Exhaustive checks
*****************

One may want to check that some code covers all possible ``Literal`` or ``Enum`` cases,
example:

.. code-block:: python

from typing import Literal

PossibleValues = Literal['one', 'two']

def validate(x: PossibleValues) -> bool:
if x == 'one':
return True
elif x == 'two':
return False
raise ValueError('Wrong values passed: {0}'.format(x))

assert validate('one') is True
assert validate('two') is False

In the code above it is really easy to make a mistake in the future:
by adding a new literal value to ``PossibleValues``,
but not adding its handler to ``validate`` function:

.. code-block:: python

PossibleValues = Literal['one', 'two', 'three']

Mypy won't catch that ``'three'`` is not covered.
However, if you want to have exhaustive check, you need to guard it properly:

.. code-block:: python

from typing import Literal, NoReturn

PossibleValues = Literal['one', 'two']

def assert_never(value: NoReturn) -> NoReturn:
# This also works in runtime as well:
assert False, 'This code should never be reached, got: {0}'.format(value)

def validate(x: PossibleValues) -> bool:
if x == 'one':
return True
elif x == 'two':
return False
assert_never(x)

In this case, when adding new values to ``PossibleValues``:

.. code-block:: python

PossibleValues = Literal['one', 'two', 'three']

Mypy will cover you:

.. code-block:: python

def validate(x: PossibleValues) -> bool:
if x == 'one':
return True
elif x == 'two':
return False
assert_never(x) # E: Argument 1 to "assert_exhaustive" has incompatible type "Literal['three']"; expected "NoReturn"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment still refers to the old name assert_exhaustive


Limitations
***********

Expand Down