Skip to content

Commit

Permalink
pythongh-123523: Rework typing documentation for generators and corou…
Browse files Browse the repository at this point in the history
…tines, and link to it from `collections.abc` docs (python#123544)
  • Loading branch information
sterliakov committed Sep 6, 2024
1 parent d343f97 commit 56e4a41
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 101 deletions.
14 changes: 14 additions & 0 deletions Doc/library/collections.abc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ Collections Abstract Base Classes -- Detailed Descriptions

ABC for classes that provide the :meth:`~object.__call__` method.

See :ref:`annotating-callables` for details on how to use
:class:`!Callable` in type annotations.

.. class:: Iterable

ABC for classes that provide the :meth:`~container.__iter__` method.
Expand Down Expand Up @@ -250,6 +253,9 @@ Collections Abstract Base Classes -- Detailed Descriptions
:meth:`~generator.send`,
:meth:`~generator.throw` and :meth:`~generator.close` methods.

See :ref:`annotating-generators-and-coroutines`
for details on using :class:`!Generator` in type annotations.

.. versionadded:: 3.5

.. class:: Sequence
Expand Down Expand Up @@ -321,6 +327,11 @@ Collections Abstract Base Classes -- Detailed Descriptions
Using ``isinstance(gencoro, Coroutine)`` for them will return ``False``.
Use :func:`inspect.isawaitable` to detect them.

See :ref:`annotating-generators-and-coroutines`
for details on using :class:`!Coroutine` in type annotations.
The variance and order of type parameters correspond to those of
:class:`Generator`.

.. versionadded:: 3.5

.. class:: AsyncIterable
Expand All @@ -342,6 +353,9 @@ Collections Abstract Base Classes -- Detailed Descriptions
ABC for :term:`asynchronous generator` classes that implement the protocol
defined in :pep:`525` and :pep:`492`.

See :ref:`annotating-generators-and-coroutines`
for details on using :class:`!AsyncGenerator` in type annotations.

.. versionadded:: 3.6

.. class:: Buffer
Expand Down
199 changes: 98 additions & 101 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Annotating callable objects
===========================

Functions -- or other :term:`callable` objects -- can be annotated using
:class:`collections.abc.Callable` or :data:`typing.Callable`.
:class:`collections.abc.Callable` or deprecated :data:`typing.Callable`.
``Callable[[int], str]`` signifies a function that takes a single parameter
of type :class:`int` and returns a :class:`str`.

Expand Down Expand Up @@ -401,7 +401,7 @@ The type of class objects
=========================

A variable annotated with ``C`` may accept a value of type ``C``. In
contrast, a variable annotated with ``type[C]`` (or
contrast, a variable annotated with ``type[C]`` (or deprecated
:class:`typing.Type[C] <Type>`) may accept values that are classes
themselves -- specifically, it will accept the *class object* of ``C``. For
example::
Expand Down Expand Up @@ -441,6 +441,87 @@ For example::
``type[Any]`` is equivalent to :class:`type`, which is the root of Python's
:ref:`metaclass hierarchy <metaclasses>`.


.. _annotating-generators-and-coroutines:

Annotating generators and coroutines
====================================

A generator can be annotated using the generic type
:class:`Generator[YieldType, SendType, ReturnType] <collections.abc.Generator>`.
For example::

def echo_round() -> Generator[int, float, str]:
sent = yield 0
while sent >= 0:
sent = yield round(sent)
return 'Done'

Note that unlike many other generic classes in the standard library,
the ``SendType`` of :class:`~collections.abc.Generator` behaves
contravariantly, not covariantly or invariantly.

The ``SendType`` and ``ReturnType`` parameters default to :const:`!None`::

def infinite_stream(start: int) -> Generator[int]:
while True:
yield start
start += 1

It is also possible to set these types explicitly::

def infinite_stream(start: int) -> Generator[int, None, None]:
while True:
yield start
start += 1

Simple generators that only ever yield values can also be annotated
as having a return type of either
:class:`Iterable[YieldType] <collections.abc.Iterable>`
or :class:`Iterator[YieldType] <collections.abc.Iterator>`::

def infinite_stream(start: int) -> Iterator[int]:
while True:
yield start
start += 1

Async generators are handled in a similar fashion, but don't
expect a ``ReturnType`` type argument
(:class:`AsyncGenerator[YieldType, SendType] <collections.abc.AsyncGenerator>`).
The ``SendType`` argument defaults to :const:`!None`, so the following definitions
are equivalent::

async def infinite_stream(start: int) -> AsyncGenerator[int]:
while True:
yield start
start = await increment(start)

async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
while True:
yield start
start = await increment(start)

As in the synchronous case,
:class:`AsyncIterable[YieldType] <collections.abc.AsyncIterable>`
and :class:`AsyncIterator[YieldType] <collections.abc.AsyncIterator>` are
available as well::

async def infinite_stream(start: int) -> AsyncIterator[int]:
while True:
yield start
start = await increment(start)

Coroutines can be annotated using
:class:`Coroutine[YieldType, SendType, ReturnType] <collections.abc.Coroutine>`.
Generic arguments correspond to those of :class:`~collections.abc.Generator`,
for example::

from collections.abc import Coroutine
c: Coroutine[list[str], str, int] # Some coroutine defined elsewhere
x = c.send('hi') # Inferred type of 'x' is list[str]
async def bar() -> None:
y = await c # Inferred type of 'y' is int

.. _user-defined-generics:

User-defined generic types
Expand Down Expand Up @@ -3318,14 +3399,9 @@ Aliases to built-in types
Deprecated alias to :class:`dict`.

Note that to annotate arguments, it is preferred
to use an abstract collection type such as :class:`Mapping`
to use an abstract collection type such as :class:`~collections.abc.Mapping`
rather than to use :class:`dict` or :class:`!typing.Dict`.

This type can be used as follows::

def count_words(text: str) -> Dict[str, int]:
...

.. deprecated:: 3.9
:class:`builtins.dict <dict>` now supports subscripting (``[]``).
See :pep:`585` and :ref:`types-genericalias`.
Expand All @@ -3335,16 +3411,9 @@ Aliases to built-in types
Deprecated alias to :class:`list`.

Note that to annotate arguments, it is preferred
to use an abstract collection type such as :class:`Sequence` or
:class:`Iterable` rather than to use :class:`list` or :class:`!typing.List`.

This type may be used as follows::

def vec2[T: (int, float)](x: T, y: T) -> List[T]:
return [x, y]

def keep_positives[T: (int, float)](vector: Sequence[T]) -> List[T]:
return [item for item in vector if item > 0]
to use an abstract collection type such as
:class:`~collections.abc.Sequence` or :class:`~collections.abc.Iterable`
rather than to use :class:`list` or :class:`!typing.List`.

.. deprecated:: 3.9
:class:`builtins.list <list>` now supports subscripting (``[]``).
Expand All @@ -3355,8 +3424,8 @@ Aliases to built-in types
Deprecated alias to :class:`builtins.set <set>`.

Note that to annotate arguments, it is preferred
to use an abstract collection type such as :class:`AbstractSet`
rather than to use :class:`set` or :class:`!typing.Set`.
to use an abstract collection type such as :class:`collections.abc.Set`
rather than to use :class:`set` or :class:`typing.Set`.

.. deprecated:: 3.9
:class:`builtins.set <set>` now supports subscripting (``[]``).
Expand Down Expand Up @@ -3544,11 +3613,6 @@ Aliases to container ABCs in :mod:`collections.abc`

Deprecated alias to :class:`collections.abc.Mapping`.

This type can be used as follows::

def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
return word_list[word]

.. deprecated:: 3.9
:class:`collections.abc.Mapping` now supports subscripting (``[]``).
See :pep:`585` and :ref:`types-genericalias`.
Expand Down Expand Up @@ -3612,14 +3676,9 @@ Aliases to asynchronous ABCs in :mod:`collections.abc`

Deprecated alias to :class:`collections.abc.Coroutine`.

The variance and order of type variables
correspond to those of :class:`Generator`, for example::

from collections.abc import Coroutine
c: Coroutine[list[str], str, int] # Some coroutine defined elsewhere
x = c.send('hi') # Inferred type of 'x' is list[str]
async def bar() -> None:
y = await c # Inferred type of 'y' is int
See :ref:`annotating-generators-and-coroutines`
for details on using :class:`collections.abc.Coroutine`
and ``typing.Coroutine`` in type annotations.

.. versionadded:: 3.5.3

Expand All @@ -3631,40 +3690,9 @@ Aliases to asynchronous ABCs in :mod:`collections.abc`

Deprecated alias to :class:`collections.abc.AsyncGenerator`.

An async generator can be annotated by the generic type
``AsyncGenerator[YieldType, SendType]``. For example::

async def echo_round() -> AsyncGenerator[int, float]:
sent = yield 0
while sent >= 0.0:
rounded = await round(sent)
sent = yield rounded

Unlike normal generators, async generators cannot return a value, so there
is no ``ReturnType`` type parameter. As with :class:`Generator`, the
``SendType`` behaves contravariantly.

The ``SendType`` defaults to :const:`!None`::

async def infinite_stream(start: int) -> AsyncGenerator[int]:
while True:
yield start
start = await increment(start)

It is also possible to set this type explicitly::

async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
while True:
yield start
start = await increment(start)

Alternatively, annotate your generator as having a return type of
either ``AsyncIterable[YieldType]`` or ``AsyncIterator[YieldType]``::

async def infinite_stream(start: int) -> AsyncIterator[int]:
while True:
yield start
start = await increment(start)
See :ref:`annotating-generators-and-coroutines`
for details on using :class:`collections.abc.AsyncGenerator`
and ``typing.AsyncGenerator`` in type annotations.

.. versionadded:: 3.6.1

Expand Down Expand Up @@ -3746,40 +3774,9 @@ Aliases to other ABCs in :mod:`collections.abc`

Deprecated alias to :class:`collections.abc.Generator`.

A generator can be annotated by the generic type
``Generator[YieldType, SendType, ReturnType]``. For example::

def echo_round() -> Generator[int, float, str]:
sent = yield 0
while sent >= 0:
sent = yield round(sent)
return 'Done'

Note that unlike many other generics in the typing module, the ``SendType``
of :class:`Generator` behaves contravariantly, not covariantly or
invariantly.

The ``SendType`` and ``ReturnType`` parameters default to :const:`!None`::

def infinite_stream(start: int) -> Generator[int]:
while True:
yield start
start += 1

It is also possible to set these types explicitly::

def infinite_stream(start: int) -> Generator[int, None, None]:
while True:
yield start
start += 1

Alternatively, annotate your generator as having a return type of
either ``Iterable[YieldType]`` or ``Iterator[YieldType]``::

def infinite_stream(start: int) -> Iterator[int]:
while True:
yield start
start += 1
See :ref:`annotating-generators-and-coroutines`
for details on using :class:`collections.abc.Generator`
and ``typing.Generator`` in type annotations.

.. deprecated:: 3.9
:class:`collections.abc.Generator` now supports subscripting (``[]``).
Expand Down

0 comments on commit 56e4a41

Please sign in to comment.