Issue #24439: Improve PEP 492 related docs.

Patch by Martin Panter.
This commit is contained in:
Yury Selivanov 2015-06-24 11:04:15 -04:00
parent fcba97242b
commit 66f8828bfc
12 changed files with 185 additions and 91 deletions

View File

@ -169,18 +169,19 @@ Glossary
statement by defining :meth:`__enter__` and :meth:`__exit__` methods. statement by defining :meth:`__enter__` and :meth:`__exit__` methods.
See :pep:`343`. See :pep:`343`.
coroutine function
A function which returns a :term:`coroutine` object. It is defined
with an :keyword:`async def` keyword, and may contain :keyword:`await`,
:keyword:`async for`, and :keyword:`async with` keywords. Introduced
by :pep:`492`.
coroutine coroutine
Coroutines is a more generalized form of subroutines. Subroutines are Coroutines is a more generalized form of subroutines. Subroutines are
entered at one point and exited at another point. Coroutines, can be entered at one point and exited at another point. Coroutines can be
entered, exited, and resumed at many different points. See entered, exited, and resumed at many different points. They can be
:keyword:`await` expressions, and :keyword:`async for` and implemented with the :keyword:`async def` statement. See also
:keyword:`async with` statements. See also :pep:`492`. :pep:`492`.
coroutine function
A function which returns a :term:`coroutine` object. A coroutine
function may be defined with the :keyword:`async def` statement,
and may contain :keyword:`await`, :keyword:`async for`, and
:keyword:`async with` keywords. These were introduced
by :pep:`492`.
CPython CPython
The canonical implementation of the Python programming language, as The canonical implementation of the Python programming language, as

View File

@ -8,17 +8,23 @@ Tasks and coroutines
Coroutines Coroutines
---------- ----------
A coroutine is a generator that follows certain conventions. For Coroutines used with :mod:`asyncio` may be implemented using the
documentation purposes, all coroutines should be decorated with :keyword:`async def` statement, or by using :term:`generators <generator>`.
``@asyncio.coroutine``, but this cannot be strictly enforced. The :keyword:`async def` type of coroutine was added in Python 3.5, and
is recommended if there is no need to support older Python versions.
Coroutines use the ``yield from`` syntax introduced in :pep:`380`, Generator-based coroutines should be decorated with :func:`@asyncio.coroutine
<asyncio.coroutine>`, although this is not strictly enforced.
The decorator enables compatibility with :keyword:`async def` coroutines,
and also serves as documentation. Generator-based
coroutines use the ``yield from`` syntax introduced in :pep:`380`,
instead of the original ``yield`` syntax. instead of the original ``yield`` syntax.
The word "coroutine", like the word "generator", is used for two The word "coroutine", like the word "generator", is used for two
different (though related) concepts: different (though related) concepts:
- The function that defines a coroutine (a function definition - The function that defines a coroutine
(a function definition using :keyword:`async def` or
decorated with ``@asyncio.coroutine``). If disambiguation is needed decorated with ``@asyncio.coroutine``). If disambiguation is needed
we will call this a *coroutine function* (:func:`iscoroutinefunction` we will call this a *coroutine function* (:func:`iscoroutinefunction`
returns ``True``). returns ``True``).
@ -30,27 +36,28 @@ different (though related) concepts:
Things a coroutine can do: Things a coroutine can do:
- ``result = yield from future`` -- suspends the coroutine until the - ``result = await future`` or ``result = yield from future`` --
suspends the coroutine until the
future is done, then returns the future's result, or raises an future is done, then returns the future's result, or raises an
exception, which will be propagated. (If the future is cancelled, exception, which will be propagated. (If the future is cancelled,
it will raise a ``CancelledError`` exception.) Note that tasks are it will raise a ``CancelledError`` exception.) Note that tasks are
futures, and everything said about futures also applies to tasks. futures, and everything said about futures also applies to tasks.
- ``result = yield from coroutine`` -- wait for another coroutine to - ``result = await coroutine`` or ``result = yield from coroutine`` --
wait for another coroutine to
produce a result (or raise an exception, which will be propagated). produce a result (or raise an exception, which will be propagated).
The ``coroutine`` expression must be a *call* to another coroutine. The ``coroutine`` expression must be a *call* to another coroutine.
- ``return expression`` -- produce a result to the coroutine that is - ``return expression`` -- produce a result to the coroutine that is
waiting for this one using ``yield from``. waiting for this one using :keyword:`await` or ``yield from``.
- ``raise exception`` -- raise an exception in the coroutine that is - ``raise exception`` -- raise an exception in the coroutine that is
waiting for this one using ``yield from``. waiting for this one using :keyword:`await` or ``yield from``.
Calling a coroutine does not start its code running -- it is just a Calling a coroutine does not start its code running --
generator, and the coroutine object returned by the call is really a the coroutine object returned by the call doesn't do anything until you
generator object, which doesn't do anything until you iterate over it. schedule its execution. There are two basic ways to start it running:
In the case of a coroutine object, there are two basic ways to start call ``await coroutine`` or ``yield from coroutine`` from another coroutine
it running: call ``yield from coroutine`` from another coroutine
(assuming the other coroutine is already running!), or schedule its execution (assuming the other coroutine is already running!), or schedule its execution
using the :func:`async` function or the :meth:`BaseEventLoop.create_task` using the :func:`async` function or the :meth:`BaseEventLoop.create_task`
method. method.
@ -60,9 +67,15 @@ Coroutines (and tasks) can only run when the event loop is running.
.. decorator:: coroutine .. decorator:: coroutine
Decorator to mark coroutines. Decorator to mark generator-based coroutines. This enables
the generator use :keyword:`!yield from` to call :keyword:`async
def` coroutines, and also enables the generator to be called by
:keyword:`async def` coroutines, for instance using an
:keyword:`await` expression.
If the coroutine is not yielded from before it is destroyed, an error There is no need to decorate :keyword:`async def` coroutines themselves.
If the generator is not yielded from before it is destroyed, an error
message is logged. See :ref:`Detect coroutines never scheduled message is logged. See :ref:`Detect coroutines never scheduled
<asyncio-coroutine-not-scheduled>`. <asyncio-coroutine-not-scheduled>`.
@ -84,8 +97,7 @@ Example of coroutine displaying ``"Hello World"``::
import asyncio import asyncio
@asyncio.coroutine async def hello_world():
def hello_world():
print("Hello World!") print("Hello World!")
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
@ -111,6 +123,21 @@ using the :meth:`sleep` function::
import asyncio import asyncio
import datetime import datetime
async def display_date(loop):
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()
The same coroutine implemented using a generator::
@asyncio.coroutine @asyncio.coroutine
def display_date(loop): def display_date(loop):
end_time = loop.time() + 5.0 end_time = loop.time() + 5.0
@ -120,11 +147,6 @@ using the :meth:`sleep` function::
break break
yield from asyncio.sleep(1) yield from asyncio.sleep(1)
loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()
.. seealso:: .. seealso::
The :ref:`display the current date with call_later() The :ref:`display the current date with call_later()
@ -139,15 +161,13 @@ Example chaining coroutines::
import asyncio import asyncio
@asyncio.coroutine async def compute(x, y):
def compute(x, y):
print("Compute %s + %s ..." % (x, y)) print("Compute %s + %s ..." % (x, y))
yield from asyncio.sleep(1.0) await asyncio.sleep(1.0)
return x + y return x + y
@asyncio.coroutine async def print_sum(x, y):
def print_sum(x, y): result = await compute(x, y)
result = yield from compute(x, y)
print("%s + %s = %s" % (x, y, result)) print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
@ -550,12 +570,14 @@ Task functions
.. function:: iscoroutine(obj) .. function:: iscoroutine(obj)
Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`. Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`,
which may be based on a generator or an :keyword:`async def` coroutine.
.. function:: iscoroutinefunction(obj) .. function:: iscoroutinefunction(func)
Return ``True`` if *func* is a decorated :ref:`coroutine function Return ``True`` if *func* is determined to be a :ref:`coroutine function
<coroutine>`. <coroutine>`, which may be a decorated generator function or an
:keyword:`async def` function.
.. coroutinefunction:: sleep(delay, result=None, \*, loop=None) .. coroutinefunction:: sleep(delay, result=None, \*, loop=None)

View File

@ -154,21 +154,22 @@ ABC Inherits from Abstract Methods Mixin
.. class:: Awaitable .. class:: Awaitable
ABC for classes that provide ``__await__`` method. Instances ABC for :term:`awaitable` objects, which can be used in :keyword:`await`
of such classes can be used in ``await`` expression. expressions. Custom implementations must provide the :meth:`__await__`
method.
:term:`coroutine` objects and instances of :term:`Coroutine` objects and instances of the
:class:`~collections.abc.Coroutine` are too instances of this ABC. :class:`~collections.abc.Coroutine` ABC are all instances of this ABC.
.. versionadded:: 3.5 .. versionadded:: 3.5
.. class:: Coroutine .. class:: Coroutine
ABC for coroutine compatible classes that implement a subset of ABC for coroutine compatible classes. These implement the
generator methods defined in :pep:`342`, namely: following methods, defined in :ref:`coroutine-objects`:
:meth:`~generator.send`, :meth:`~generator.throw`, :meth:`~coroutine.send`, :meth:`~coroutine.throw`, and
:meth:`~generator.close` methods. :meth:`__await__` must also be :meth:`~coroutine.close`. Custom implementations must also implement
implemented. All :class:`Coroutine` instances are also instances of :meth:`__await__`. All :class:`Coroutine` instances are also instances of
:class:`Awaitable`. See also the definition of :term:`coroutine`. :class:`Awaitable`. See also the definition of :term:`coroutine`.
.. versionadded:: 3.5 .. versionadded:: 3.5

View File

@ -516,12 +516,14 @@ the original TOS1.
Implements ``del TOS1[TOS]``. Implements ``del TOS1[TOS]``.
**Coroutines opcodes** **Coroutine opcodes**
.. opcode:: GET_AWAITABLE .. opcode:: GET_AWAITABLE
Implements ``TOS = get_awaitable(TOS)``; where ``get_awaitable(o)`` Implements ``TOS = get_awaitable(TOS)``, where ``get_awaitable(o)``
returns ``o`` if ``o`` is a coroutine object; or resolved ``o.__await__``. returns ``o`` if ``o`` is a coroutine object or a generator object with
the CO_ITERABLE_COROUTINE flag, or resolves
``o.__await__``.
.. opcode:: GET_AITER .. opcode:: GET_AITER

View File

@ -162,7 +162,8 @@ The following exceptions are the exceptions that are usually raised.
.. exception:: GeneratorExit .. exception:: GeneratorExit
Raised when a :term:`generator`\'s :meth:`close` method is called. It Raised when a :term:`generator` or :term:`coroutine` is closed;
see :meth:`generator.close` and :meth:`coroutine.close`. It
directly inherits from :exc:`BaseException` instead of :exc:`Exception` since directly inherits from :exc:`BaseException` instead of :exc:`Exception` since
it is technically not an error. it is technically not an error.
@ -306,7 +307,8 @@ The following exceptions are the exceptions that are usually raised.
given as an argument when constructing the exception, and defaults given as an argument when constructing the exception, and defaults
to :const:`None`. to :const:`None`.
When a generator function returns, a new :exc:`StopIteration` instance is When a :term:`generator` or :term:`coroutine` function
returns, a new :exc:`StopIteration` instance is
raised, and the value returned by the function is used as the raised, and the value returned by the function is used as the
:attr:`value` parameter to the constructor of the exception. :attr:`value` parameter to the constructor of the exception.

View File

@ -1080,7 +1080,7 @@ always available.
:func:`types.coroutine` or :func:`asyncio.coroutine` will not be :func:`types.coroutine` or :func:`asyncio.coroutine` will not be
intercepted). intercepted).
*wrapper* must be either: The *wrapper* argument must be either:
* a callable that accepts one argument (a coroutine object); * a callable that accepts one argument (a coroutine object);
* ``None``, to reset the wrapper. * ``None``, to reset the wrapper.
@ -1096,7 +1096,8 @@ always available.
return wrap(coro) return wrap(coro)
sys.set_coroutine_wrapper(wrapper) sys.set_coroutine_wrapper(wrapper)
async def foo(): pass async def foo():
pass
# The following line will fail with a RuntimeError, because # The following line will fail with a RuntimeError, because
# `wrapper` creates a `wrap(coro)` coroutine: # `wrapper` creates a `wrap(coro)` coroutine:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -281,15 +281,23 @@ Additional Utility Classes and Functions
.. versionadded:: 3.4 .. versionadded:: 3.4
Coroutines Utility Functions Coroutine Utility Functions
---------------------------- ---------------------------
.. function:: coroutine(gen_func) .. function:: coroutine(gen_func)
The function transforms a generator function to a :term:`coroutine function`, This function transforms a :term:`generator` function into a
so that it returns a :term:`coroutine` object. :term:`coroutine function` which returns a generator-based coroutine.
The generator-based coroutine is still a :term:`generator iterator`,
but is also considered to be a :term:`coroutine` object and is
:term:`awaitable`. However, it may not necessarily implement
the :meth:`__await__` method.
*gen_func* is modified in-place, hence the function can be used as a If *gen_func* is a generator function, it will be modified in-place.
decorator.
If *gen_func* is not a generator function, it will be wrapped. If it
returns an instance of :class:`collections.abc.Generator`, the instance
will be wrapped in an *awaitable* proxy object. All other types
of objects will be returned as is.
.. versionadded:: 3.5 .. versionadded:: 3.5

View File

@ -666,15 +666,9 @@ can be used to create instance variables with different implementation details.
Coroutines Coroutines
========== ==========
.. index::
statement: async def
statement: async for
statement: async with
keyword: async
keyword: await
.. versionadded:: 3.5 .. versionadded:: 3.5
.. index:: statement: async def
.. _`async def`: .. _`async def`:
Coroutine function definition Coroutine function definition
@ -683,14 +677,23 @@ Coroutine function definition
.. productionlist:: .. productionlist::
async_funcdef: "async" `funcdef` async_funcdef: "async" `funcdef`
.. index::
keyword: async
keyword: await
Execution of Python coroutines can be suspended and resumed at many points Execution of Python coroutines can be suspended and resumed at many points
(see :term:`coroutine`.) :keyword:`await` expressions, :keyword:`async for` (see :term:`coroutine`). In the body of a coroutine, any ``await`` and
and :keyword:`async with` can only be used in their bodies. ``async`` identifiers become reserved keywords; :keyword:`await` expressions,
:keyword:`async for` and :keyword:`async with` can only be used in
coroutine bodies. However, to simplify the parser, these keywords cannot
be used on the same line as a function or coroutine (:keyword:`def`
statement) header.
Functions defined with ``async def`` syntax are always coroutine functions, Functions defined with ``async def`` syntax are always coroutine functions,
even if they do not contain ``await`` or ``async`` keywords. even if they do not contain ``await`` or ``async`` keywords.
It is a :exc:`SyntaxError` to use :keyword:`yield` expressions in coroutines. It is a :exc:`SyntaxError` to use :keyword:`yield` expressions in
``async def`` coroutines.
An example of a coroutine function:: An example of a coroutine function::
@ -699,6 +702,7 @@ An example of a coroutine function::
await some_coroutine() await some_coroutine()
.. index:: statement: async for
.. _`async for`: .. _`async for`:
The :keyword:`async for` statement The :keyword:`async for` statement
@ -742,6 +746,7 @@ It is a :exc:`SyntaxError` to use ``async for`` statement outside of an
:keyword:`async def` function. :keyword:`async def` function.
.. index:: statement: async with
.. _`async with`: .. _`async with`:
The :keyword:`async with` statement The :keyword:`async with` statement

View File

@ -624,7 +624,7 @@ Callable types
a :dfn:`coroutine function`. Such a function, when called, returns a a :dfn:`coroutine function`. Such a function, when called, returns a
:term:`coroutine` object. It may contain :keyword:`await` expressions, :term:`coroutine` object. It may contain :keyword:`await` expressions,
as well as :keyword:`async with` and :keyword:`async for` statements. See as well as :keyword:`async with` and :keyword:`async for` statements. See
also :ref:`coroutines` section. also the :ref:`coroutine-objects` section.
Built-in functions Built-in functions
.. index:: .. index::
@ -2264,26 +2264,25 @@ special methods (the special method *must* be set on the class
object itself in order to be consistently invoked by the interpreter). object itself in order to be consistently invoked by the interpreter).
.. _coroutines: .. index::
single: coroutine
Coroutines Coroutines
========== ==========
.. index::
single: coroutine
Awaitable Objects Awaitable Objects
----------------- -----------------
An *awaitable* object can be one of the following: An :term:`awaitable` object generally implements an :meth:`__await__` method.
:term:`Coroutine` objects returned from :keyword:`async def` functions
are awaitable.
* A :term:`coroutine` object returned from a :term:`coroutine function`. .. note::
* A :term:`generator` decorated with :func:`types.coroutine` The :term:`generator iterator` objects returned from generators
(or :func:`asyncio.coroutine`) decorator. decorated with :func:`types.coroutine` or :func:`asyncio.coroutine`
are also awaitable, but they do not implement :meth:`__await__`.
* An object that implements an ``__await__`` method.
.. method:: object.__await__(self) .. method:: object.__await__(self)
@ -2296,6 +2295,58 @@ An *awaitable* object can be one of the following:
.. seealso:: :pep:`492` for additional information about awaitable objects. .. seealso:: :pep:`492` for additional information about awaitable objects.
.. _coroutine-objects:
Coroutine Objects
-----------------
:term:`Coroutine` objects are :term:`awaitable` objects.
A coroutine's execution can be controlled by calling :meth:`__await__` and
iterating over the result. When the coroutine has finished executing and
returns, the iterator raises :exc:`StopIteration`, and the exception's
:attr:`~StopIteration.value` attribute holds the return value. If the
coroutine raises an exception, it is propagated by the iterator. Coroutines
should not directly raise unhandled :exc:`StopIteration` exceptions.
Coroutines also have the methods listed below, which are analogous to
those of generators (see :ref:`generator-methods`). However, unlike
generators, coroutines do not directly support iteration.
.. method:: coroutine.send(value)
Starts or resumes execution of the coroutine. If *value* is ``None``,
this is equivalent to advancing the iterator returned by
:meth:`__await__`. If *value* is not ``None``, this method delegates
to the :meth:`~generator.send` method of the iterator that caused
the coroutine to suspend. The result (return value,
:exc:`StopIteration`, or other exception) is the same as when
iterating over the :meth:`__await__` return value, described above.
.. method:: coroutine.throw(type[, value[, traceback]])
Raises the specified exception in the coroutine. This method delegates
to the :meth:`~generator.throw` method of the iterator that caused
the coroutine to suspend, if it has such a method. Otherwise,
the exception is raised at the suspension point. The result
(return value, :exc:`StopIteration`, or other exception) is the same as
when iterating over the :meth:`__await__` return value, described
above. If the exception is not caught in the coroutine, it propagates
back to the caller.
.. method:: coroutine.close()
Causes the coroutine to clean itself up and exit. If the coroutine
is suspended, this method first delegates to the :meth:`~generator.close`
method of the iterator that caused the coroutine to suspend, if it
has such a method. Then it raises :exc:`GeneratorExit` at the
suspension point, causing the coroutine to immediately clean itself up.
Finally, the coroutine is marked as having finished executing, even if
it was never started.
Coroutine objects are automatically closed using the above process when
they are about to be destroyed.
Asynchronous Iterators Asynchronous Iterators
---------------------- ----------------------

View File

@ -390,6 +390,7 @@ on the right hand side of an assignment statement.
to sub-generators easy. to sub-generators easy.
.. index:: object: generator .. index:: object: generator
.. _generator-methods:
Generator-iterator methods Generator-iterator methods
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -793,11 +793,11 @@ static PyMemberDef coro_memberlist[] = {
PyDoc_STRVAR(coro_send_doc, PyDoc_STRVAR(coro_send_doc,
"send(arg) -> send 'arg' into coroutine,\n\ "send(arg) -> send 'arg' into coroutine,\n\
return next yielded value or raise StopIteration."); return next iterated value or raise StopIteration.");
PyDoc_STRVAR(coro_throw_doc, PyDoc_STRVAR(coro_throw_doc,
"throw(typ[,val[,tb]]) -> raise exception in coroutine,\n\ "throw(typ[,val[,tb]]) -> raise exception in coroutine,\n\
return next yielded value or raise StopIteration."); return next iterated value or raise StopIteration.");
PyDoc_STRVAR(coro_close_doc, PyDoc_STRVAR(coro_close_doc,
"close() -> raise GeneratorExit inside coroutine."); "close() -> raise GeneratorExit inside coroutine.");
@ -908,9 +908,9 @@ coro_wrapper_traverse(PyCoroWrapper *cw, visitproc visit, void *arg)
} }
static PyMethodDef coro_wrapper_methods[] = { static PyMethodDef coro_wrapper_methods[] = {
{"send",(PyCFunction)coro_wrapper_send, METH_O, send_doc}, {"send",(PyCFunction)coro_wrapper_send, METH_O, coro_send_doc},
{"throw",(PyCFunction)coro_wrapper_throw, METH_VARARGS, throw_doc}, {"throw",(PyCFunction)coro_wrapper_throw, METH_VARARGS, coro_throw_doc},
{"close",(PyCFunction)coro_wrapper_close, METH_NOARGS, close_doc}, {"close",(PyCFunction)coro_wrapper_close, METH_NOARGS, coro_close_doc},
{NULL, NULL} /* Sentinel */ {NULL, NULL} /* Sentinel */
}; };