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

Minor documentation updates #12329

Merged
merged 4 commits into from Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/source/config_file.rst
Expand Up @@ -201,7 +201,7 @@ section of the command line docs.

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

.. code-block:: ini
Expand Down Expand Up @@ -237,7 +237,7 @@ section of the command line docs.
[tool.mypy]
exclude = [
"^one\.py$", # TOML's double-quoted strings require escaping backslashes
"^one\\.py$", # TOML's double-quoted strings require escaping backslashes
Copy link
Member

Choose a reason for hiding this comment

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

Should the next line change too? Haven't tested this myself

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The comment indicates that single-quoted strings are different. I'll test his.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, single quotes behave differently, so the current example seems correct.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Sorry for missing the comment.

'two\.pyi$', # but TOML's single-quoted strings do not
'^three\.',
]
Expand Down
54 changes: 42 additions & 12 deletions docs/source/literal_types.rst
Expand Up @@ -292,8 +292,8 @@ using ``isinstance()``:
This feature is sometimes called "sum types" or "discriminated union types"
in other programming languages.

Exhaustiveness checks
*********************
Exhaustiveness checking
***********************

You may want to check that some code covers all possible
``Literal`` or ``Enum`` cases. Example:
Expand Down Expand Up @@ -359,6 +359,35 @@ mypy will spot the error:
# expected "NoReturn"
assert_never(x)
If runtime checking against unexpected values is not needed, you can
leave out the ``assert_never`` call in the above example, and mypy
will still generate an error about function ``validate`` returning
without a value:

.. code-block:: python
PossibleValues = Literal['one', 'two', 'three']
# Error: Missing return statement
def validate(x: PossibleValues) -> bool:
if x == 'one':
return True
elif x == 'two':
return False
Exhaustiveness checking is also supported for match statements (Python 3.10 and later):

.. code-block:: python
def validate(x: PossibleValues) -> bool:
match x:
case 'one':
return True
case 'two':
return False
assert_never(x)
Limitations
***********

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

Similiar to ``Literal`` types ``Enum`` supports exhaustive checks.
Similar to ``Literal`` types, ``Enum`` supports exhaustiveness checking.
Let's start with a definition:

.. code-block:: python
Expand All @@ -423,21 +452,22 @@ Let's start with a definition:
up = 'up'
down = 'down'
Now, let's define an exhaustive check:
Now, let's use an exhaustiveness check:

.. code-block:: python
def choose_direction(direction: Direction) -> None:
if direction is Direction.up:
reveal_type(direction) # N: Revealed type is "Literal[ex.Direction.up]"
reveal_type(direction) # N: Revealed type is "Literal[Direction.up]"
print('Going up!')
return
elif direction is Direction.down:
print('Down')
return
# This line is never reached
assert_never(direction)
And then test that it raises an error when some cases are not covered:
If we forget to handle one of the cases, mypy will generate an error:

.. code-block:: python
Expand All @@ -447,13 +477,13 @@ And then test that it raises an error when some cases are not covered:
return
assert_never(direction) # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn"
Exhaustiveness checking is also supported for match statements (Python 3.10 and later).

Extra Enum checks
*****************

Mypy also tries to support special features of ``Enum``
the same way Python's runtime does.

Extra checks:
the same way Python's runtime does:

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

.. code-block:: python
Expand Down
90 changes: 23 additions & 67 deletions docs/source/more_types.rst
Expand Up @@ -874,68 +874,6 @@ value of type :py:class:`Coroutine[Any, Any, T] <typing.Coroutine>`, which is a
:ref:`reveal_type() <reveal-type>` displays the inferred static type of
an expression.

If you want to use coroutines in Python 3.4, which does not support
the ``async def`` syntax, you can instead use the :py:func:`@asyncio.coroutine <asyncio.coroutine>`
decorator to convert a generator into a coroutine.

Note that we set the ``YieldType`` of the generator to be ``Any`` in the
following example. This is because the exact yield type is an implementation
detail of the coroutine runner (e.g. the :py:mod:`asyncio` event loop) and your
coroutine shouldn't have to know or care about what precisely that type is.

.. code-block:: python
from typing import Any, Generator
import asyncio
@asyncio.coroutine
def countdown_2(tag: str, count: int) -> Generator[Any, None, str]:
while count > 0:
print('T-minus {} ({})'.format(count, tag))
yield from asyncio.sleep(0.1)
count -= 1
return "Blastoff!"
loop = asyncio.get_event_loop()
loop.run_until_complete(countdown_2("USS Enterprise", 5))
loop.close()
As before, the result of calling a generator decorated with :py:func:`@asyncio.coroutine <asyncio.coroutine>`
will be a value of type :py:class:`Awaitable[T] <typing.Awaitable>`.

.. note::

At runtime, you are allowed to add the :py:func:`@asyncio.coroutine <asyncio.coroutine>` decorator to
both functions and generators. This is useful when you want to mark a
work-in-progress function as a coroutine, but have not yet added ``yield`` or
``yield from`` statements:

.. code-block:: python
import asyncio
@asyncio.coroutine
def serialize(obj: object) -> str:
# todo: add yield/yield from to turn this into a generator
return "placeholder"
However, mypy currently does not support converting functions into
coroutines. Support for this feature will be added in a future version, but
for now, you can manually force the function to be a generator by doing
something like this:

.. code-block:: python
from typing import Generator
import asyncio
@asyncio.coroutine
def serialize(obj: object) -> Generator[None, None, str]:
# todo: add yield/yield from to turn this into a generator
if False:
yield
return "placeholder"
You may also choose to create a subclass of :py:class:`~typing.Awaitable` instead:

.. code-block:: python
Expand Down Expand Up @@ -995,11 +933,29 @@ To create an iterable coroutine, subclass :py:class:`~typing.AsyncIterator`:
loop.run_until_complete(countdown_4("Serenity", 5))
loop.close()
For a more concrete example, the mypy repo has a toy webcrawler that
demonstrates how to work with coroutines. One version
`uses async/await <https://github.com/python/mypy/blob/master/test-data/samples/crawl2.py>`_
and one
`uses yield from <https://github.com/python/mypy/blob/master/test-data/samples/crawl.py>`_.
If you use coroutines in legacy code that was originally written for
Python 3.4, which did not support the ``async def`` syntax, you would
instead use the :py:func:`@asyncio.coroutine <asyncio.coroutine>`
decorator to convert a generator into a coroutine, and use a
generator type as the return type:

.. code-block:: python
from typing import Any, Generator
import asyncio
@asyncio.coroutine
def countdown_2(tag: str, count: int) -> Generator[Any, None, str]:
while count > 0:
print('T-minus {} ({})'.format(count, tag))
yield from asyncio.sleep(0.1)
count -= 1
return "Blastoff!"
loop = asyncio.get_event_loop()
loop.run_until_complete(countdown_2("USS Enterprise", 5))
loop.close()
.. _typeddict:

Expand Down