Skip to content

Commit fb75cdc

Browse files
authored
Adds docs about exhaustive literal and enum checks (#10860)
This feature is not-really known from my experience. I had to explain it several times to other devs. But, I think that this technique should be widely recognised! It is awesome! Refs #6366
1 parent 5d84eea commit fb75cdc

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

docs/source/literal_types.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,73 @@ using ``isinstance()``:
289289
This feature is sometimes called "sum types" or "discriminated union types"
290290
in other programming languages.
291291

292+
Exhaustive checks
293+
*****************
294+
295+
One may want to check that some code covers all possible ``Literal`` or ``Enum`` cases,
296+
example:
297+
298+
.. code-block:: python
299+
300+
from typing import Literal
301+
302+
PossibleValues = Literal['one', 'two']
303+
304+
def validate(x: PossibleValues) -> bool:
305+
if x == 'one':
306+
return True
307+
elif x == 'two':
308+
return False
309+
raise ValueError('Wrong values passed: {0}'.format(x))
310+
311+
assert validate('one') is True
312+
assert validate('two') is False
313+
314+
In the code above it is really easy to make a mistake in the future:
315+
by adding a new literal value to ``PossibleValues``,
316+
but not adding its handler to ``validate`` function:
317+
318+
.. code-block:: python
319+
320+
PossibleValues = Literal['one', 'two', 'three']
321+
322+
Mypy won't catch that ``'three'`` is not covered.
323+
However, if you want to have exhaustive check, you need to guard it properly:
324+
325+
.. code-block:: python
326+
327+
from typing import Literal, NoReturn
328+
329+
PossibleValues = Literal['one', 'two']
330+
331+
def assert_never(value: NoReturn) -> NoReturn:
332+
# This also works in runtime as well:
333+
assert False, 'This code should never be reached, got: {0}'.format(value)
334+
335+
def validate(x: PossibleValues) -> bool:
336+
if x == 'one':
337+
return True
338+
elif x == 'two':
339+
return False
340+
assert_never(x)
341+
342+
In this case, when adding new values to ``PossibleValues``:
343+
344+
.. code-block:: python
345+
346+
PossibleValues = Literal['one', 'two', 'three']
347+
348+
Mypy will cover you:
349+
350+
.. code-block:: python
351+
352+
def validate(x: PossibleValues) -> bool:
353+
if x == 'one':
354+
return True
355+
elif x == 'two':
356+
return False
357+
assert_never(x) # E: Argument 1 to "assert_never" has incompatible type "Literal['three']"; expected "NoReturn"
358+
292359
Limitations
293360
***********
294361

0 commit comments

Comments
 (0)