Skip to content

Commit 942395a

Browse files
JukkaLJelleZijlstra
andcommitted
Minor documentation updates (#12329)
* Some minor documentation updates * Add more discussion of exhaustiveness checking * Update docs/source/literal_types.rst * Simplify docs for legacy async Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 5954bc9 commit 942395a

File tree

3 files changed

+67
-81
lines changed

3 files changed

+67
-81
lines changed

docs/source/config_file.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ section of the command line docs.
201201

202202
A regular expression that matches file names, directory names and paths
203203
which mypy should ignore while recursively discovering files to check.
204-
Use forward slashes on all platforms.
204+
Use forward slashes (``/``) as directory separators on all platforms.
205205

206206
.. code-block:: ini
207207
@@ -237,7 +237,7 @@ section of the command line docs.
237237
238238
[tool.mypy]
239239
exclude = [
240-
"^one\.py$", # TOML's double-quoted strings require escaping backslashes
240+
"^one\\.py$", # TOML's double-quoted strings require escaping backslashes
241241
'two\.pyi$', # but TOML's single-quoted strings do not
242242
'^three\.',
243243
]

docs/source/literal_types.rst

+42-12
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ using ``isinstance()``:
292292
This feature is sometimes called "sum types" or "discriminated union types"
293293
in other programming languages.
294294

295-
Exhaustiveness checks
296-
*********************
295+
Exhaustiveness checking
296+
***********************
297297

298298
You may want to check that some code covers all possible
299299
``Literal`` or ``Enum`` cases. Example:
@@ -359,6 +359,35 @@ mypy will spot the error:
359359
# expected "NoReturn"
360360
assert_never(x)
361361
362+
If runtime checking against unexpected values is not needed, you can
363+
leave out the ``assert_never`` call in the above example, and mypy
364+
will still generate an error about function ``validate`` returning
365+
without a value:
366+
367+
.. code-block:: python
368+
369+
PossibleValues = Literal['one', 'two', 'three']
370+
371+
# Error: Missing return statement
372+
def validate(x: PossibleValues) -> bool:
373+
if x == 'one':
374+
return True
375+
elif x == 'two':
376+
return False
377+
378+
Exhaustiveness checking is also supported for match statements (Python 3.10 and later):
379+
380+
.. code-block:: python
381+
382+
def validate(x: PossibleValues) -> bool:
383+
match x:
384+
case 'one':
385+
return True
386+
case 'two':
387+
return False
388+
assert_never(x)
389+
390+
362391
Limitations
363392
***********
364393

@@ -404,10 +433,10 @@ You can use enums to annotate types as you would expect:
404433
Movement(Direction.up, 5.0) # ok
405434
Movement('up', 5.0) # E: Argument 1 to "Movemement" has incompatible type "str"; expected "Direction"
406435
407-
Exhaustive checks
408-
*****************
436+
Exhaustiveness checking
437+
***********************
409438

410-
Similiar to ``Literal`` types ``Enum`` supports exhaustive checks.
439+
Similar to ``Literal`` types, ``Enum`` supports exhaustiveness checking.
411440
Let's start with a definition:
412441

413442
.. code-block:: python
@@ -423,21 +452,22 @@ Let's start with a definition:
423452
up = 'up'
424453
down = 'down'
425454
426-
Now, let's define an exhaustive check:
455+
Now, let's use an exhaustiveness check:
427456

428457
.. code-block:: python
429458
430459
def choose_direction(direction: Direction) -> None:
431460
if direction is Direction.up:
432-
reveal_type(direction) # N: Revealed type is "Literal[ex.Direction.up]"
461+
reveal_type(direction) # N: Revealed type is "Literal[Direction.up]"
433462
print('Going up!')
434463
return
435464
elif direction is Direction.down:
436465
print('Down')
437466
return
467+
# This line is never reached
438468
assert_never(direction)
439469
440-
And then test that it raises an error when some cases are not covered:
470+
If we forget to handle one of the cases, mypy will generate an error:
441471

442472
.. code-block:: python
443473
@@ -447,13 +477,13 @@ And then test that it raises an error when some cases are not covered:
447477
return
448478
assert_never(direction) # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn"
449479
480+
Exhaustiveness checking is also supported for match statements (Python 3.10 and later).
481+
450482
Extra Enum checks
451483
*****************
452484

453485
Mypy also tries to support special features of ``Enum``
454-
the same way Python's runtime does.
455-
456-
Extra checks:
486+
the same way Python's runtime does:
457487

458488
- Any ``Enum`` class with values is implicitly :ref:`final <final_attrs>`.
459489
This is what happens in CPython:
@@ -467,7 +497,7 @@ Extra checks:
467497
...
468498
TypeError: Other: cannot extend enumeration 'Some'
469499
470-
We do the same thing:
500+
Mypy also catches this error:
471501

472502
.. code-block:: python
473503

docs/source/more_types.rst

+23-67
Original file line numberDiff line numberDiff line change
@@ -874,68 +874,6 @@ value of type :py:class:`Coroutine[Any, Any, T] <typing.Coroutine>`, which is a
874874
:ref:`reveal_type() <reveal-type>` displays the inferred static type of
875875
an expression.
876876

877-
If you want to use coroutines in Python 3.4, which does not support
878-
the ``async def`` syntax, you can instead use the :py:func:`@asyncio.coroutine <asyncio.coroutine>`
879-
decorator to convert a generator into a coroutine.
880-
881-
Note that we set the ``YieldType`` of the generator to be ``Any`` in the
882-
following example. This is because the exact yield type is an implementation
883-
detail of the coroutine runner (e.g. the :py:mod:`asyncio` event loop) and your
884-
coroutine shouldn't have to know or care about what precisely that type is.
885-
886-
.. code-block:: python
887-
888-
from typing import Any, Generator
889-
import asyncio
890-
891-
@asyncio.coroutine
892-
def countdown_2(tag: str, count: int) -> Generator[Any, None, str]:
893-
while count > 0:
894-
print('T-minus {} ({})'.format(count, tag))
895-
yield from asyncio.sleep(0.1)
896-
count -= 1
897-
return "Blastoff!"
898-
899-
loop = asyncio.get_event_loop()
900-
loop.run_until_complete(countdown_2("USS Enterprise", 5))
901-
loop.close()
902-
903-
As before, the result of calling a generator decorated with :py:func:`@asyncio.coroutine <asyncio.coroutine>`
904-
will be a value of type :py:class:`Awaitable[T] <typing.Awaitable>`.
905-
906-
.. note::
907-
908-
At runtime, you are allowed to add the :py:func:`@asyncio.coroutine <asyncio.coroutine>` decorator to
909-
both functions and generators. This is useful when you want to mark a
910-
work-in-progress function as a coroutine, but have not yet added ``yield`` or
911-
``yield from`` statements:
912-
913-
.. code-block:: python
914-
915-
import asyncio
916-
917-
@asyncio.coroutine
918-
def serialize(obj: object) -> str:
919-
# todo: add yield/yield from to turn this into a generator
920-
return "placeholder"
921-
922-
However, mypy currently does not support converting functions into
923-
coroutines. Support for this feature will be added in a future version, but
924-
for now, you can manually force the function to be a generator by doing
925-
something like this:
926-
927-
.. code-block:: python
928-
929-
from typing import Generator
930-
import asyncio
931-
932-
@asyncio.coroutine
933-
def serialize(obj: object) -> Generator[None, None, str]:
934-
# todo: add yield/yield from to turn this into a generator
935-
if False:
936-
yield
937-
return "placeholder"
938-
939877
You may also choose to create a subclass of :py:class:`~typing.Awaitable` instead:
940878

941879
.. code-block:: python
@@ -995,11 +933,29 @@ To create an iterable coroutine, subclass :py:class:`~typing.AsyncIterator`:
995933
loop.run_until_complete(countdown_4("Serenity", 5))
996934
loop.close()
997935
998-
For a more concrete example, the mypy repo has a toy webcrawler that
999-
demonstrates how to work with coroutines. One version
1000-
`uses async/await <https://github.com/python/mypy/blob/master/test-data/samples/crawl2.py>`_
1001-
and one
1002-
`uses yield from <https://github.com/python/mypy/blob/master/test-data/samples/crawl.py>`_.
936+
If you use coroutines in legacy code that was originally written for
937+
Python 3.4, which did not support the ``async def`` syntax, you would
938+
instead use the :py:func:`@asyncio.coroutine <asyncio.coroutine>`
939+
decorator to convert a generator into a coroutine, and use a
940+
generator type as the return type:
941+
942+
.. code-block:: python
943+
944+
from typing import Any, Generator
945+
import asyncio
946+
947+
@asyncio.coroutine
948+
def countdown_2(tag: str, count: int) -> Generator[Any, None, str]:
949+
while count > 0:
950+
print('T-minus {} ({})'.format(count, tag))
951+
yield from asyncio.sleep(0.1)
952+
count -= 1
953+
return "Blastoff!"
954+
955+
loop = asyncio.get_event_loop()
956+
loop.run_until_complete(countdown_2("USS Enterprise", 5))
957+
loop.close()
958+
1003959
1004960
.. _typeddict:
1005961

0 commit comments

Comments
 (0)