classification
Title: Feedback for awaitable coroutine documentation
Type: enhancement Stage: resolved
Components: Documentation Versions: Python 3.6, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: 24400 Superseder:
Assigned To: yselivanov Nosy List: asvetlov, docs@python, gvanrossum, martin.panter, ncoghlan, python-dev, vstinner, yselivanov
Priority: normal Keywords: patch

Created on 2015-06-12 14:46 by martin.panter, last changed 2015-06-25 15:51 by yselivanov. This issue is now closed.

Files
File name Uploaded Description Edit
async-doc.patch martin.panter, 2015-06-16 03:41 review
async-doc.v2.patch martin.panter, 2015-06-23 12:26 review
async-doc.v3.patch martin.panter, 2015-06-24 13:11 review
tulip_coro.png martin.panter, 2015-06-24 13:13 Diagram included in patch v3
tulip_coro.dia martin.panter, 2015-06-24 23:38
Messages (13)
msg245256 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-12 14:46
Today I tried playing with the new “async def” functionality in the interactive interpreter, based on reading the documentation. Here are some bits that surprised me, so could be the focus of further documentation. (Initial documentation added in Issue 24180.)

1. Confusion between coroutine and generator terms

<coroutine object f at 0x7f9a5ae45200>
AttributeError: 'generator' object has no attribute '__await__'
<class 'generator'>
TypeError: coroutine-objects do not support iteration
TypeError: can't send non-None value to a just-started generator

Is it a coroutine object, or a generator object? The error messages etc seem to be split between the two terms. I guess the underlying implementation is closely shared between the two types, so old error messages still say “generator”, while new ones say “coroutine”. So there may not be an easy fix, but it was initially disconcerting. Perhaps a statement somewhere explaining coroutine instances are (based on / modified / a kind of / evolved from) generator instance objects, as appropriate.

2. Properties of a coroutine instance object

<https://docs.python.org/dev/reference/datamodel.html#awaitable-objects> suggests that a coroutine instance should implement __await__(), but it apparently does not.

How to drive through the coroutine’s await points? This should be documented, and linked from relevant places in the documentation; perhaps the “coroutine” glossary entry, “async” statement, and “await” expression. The best clue that I found in the current documentation was <https://docs.python.org/dev/library/collections.abc.html#collections.abc.Coroutine>, suggesting to use send().

Without having studied the PEP recently, I was expecting __await__() to be implemented, and invoking it would be similar to calling next() on an old-skool generator-iterator. Instead I see that calling __await__() is semantically more like __iter__(): it is only invoked once per “await” expression, and the return value is iterated over.

3. Allergic reaction to “await” on the same line as “async def”

>>> async def f(): await A(())
SyntaxError: invalid syntax

I see this is mentioned in the PEP that this is necessary so that “await” doesn’t have to be made a strict language keyword. But it should also be in the documentation.

4. Coroutine instances don’t implement the Coroutine ABC

>>> isinstance(c, Coroutine)
True
>>> hasattr(Coroutine, "__await__")
True
>>> hasattr(c, "__await__")
False

Reading closely, the documentation says “coroutine compatible classes”, but given the liberal use of “coroutine” everywhere, I think there should be a more obvious warning of how much a coroutine is a Coroutine.
msg245415 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-16 03:41
It seems that Issue 24400 may implement __await__() for native coroutine instances, making points 1, 2 and 4 mainly redundant. This would also bypass a fifth problem: the need for the mandatory yet largely useless send(None) argument.

I am posting async-doc.patch, with these changes:

* Distinguish between PEP 492’s “native coroutines” and other coroutines such as those already supported by asyncio and PEP 342 (“yield” expression and generator cleanup)
* Move “coroutine” before “coroutine function” in the glossary.
* Add links to “coroutine” glossary
* Point 3: Explain about “async” and “await” becoming reserved keywords after a “def” header line
* Part of point 2: List native coroutine instance methods and hint at relationship with generator iterator instances

Still to do: how to drive an awaitable coroutine. Currently it seems you have to call coro.send(None), but if the current patch for Issue 24400 were applied, I think it would become next(coro), so I will leave this for later.
msg245442 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-06-17 16:52
Martin, thanks a lot for the feedback and patch! I'll review the patch when issue24400 lands.
msg245616 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-06-22 03:43
Martin, if you want to help with the documentation, it would be great if you can help me with updating asyncio coroutines section: https://docs.python.org/3.5/library/asyncio-task.html#coroutines
msg245624 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-22 12:00
When I update my patch I can try updating the asyncio section. However I have only had limited experience with asyncio, so feel free to suggest things to add. Here is a list of things I think may need changing:

* “async def” routines are allowed in addition to generators in asyncio (e.g. in Task constructor)
* Other awaitables are also accepted as asyncio coroutines
* List of “yield from” actions could be augmented with “await”, “async for”, etc
* The coroutines provided by asyncio are both iterable and awaitable, so they may be used with both “yield from” and “await”
* Change references of asyncio.async() to ensure_future()
msg245642 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-06-22 18:34
Hi Martin,

I've left you some feedback in the code review.

> * “async def” routines are allowed in addition to generators in asyncio (e.g. in Task constructor)

Right. I think we need to add some code samples too.

> * Other awaitables are also accepted as asyncio coroutines

Depending on where, I guess. asyncio.Task should only accept when asyncio.iscoroutine is true -- abc.Coroutine, types.GeneratorType.

> * List of “yield from” actions could be augmented with “await”, “async for”, etc

Not sure what you mean here.

> * The coroutines provided by asyncio are both iterable and awaitable, so they may be used with both “yield from” and “await”

Right.

We also need to make sure that it's documented that in order to have "yield from native_coro()", you have to decorate the gen function with 'asyncio.coroutine'.

And we should mention that if you're targeting Python 3.5+ you should use the new syntax.

> * Change references of asyncio.async() to ensure_future()

This is already done ;)

Thanks!
msg245680 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-23 12:26
Here is a patch with more improvements to the coroutine documentation, including:

* Remove expansion of “coroutine” glossary to include generators; it was not my original intention of this bug. Perhaps this can be dealt with separately in Issue 24087 if other people agree there is a problem.
* Add Native Coroutine Objects section to /Doc/reference/datamodel.rst describing each method
* Tweak coroutine and wrapper object doc strings to avoid generator terms
* Mention native coroutines and “await” in the asyncio Coroutines section, where the existing text only mentions generators and “yield from”
* Also recommend native coroutines, @asyncio.coroutine for calling native coroutines, etc

I changed two of the asyncio coroutine examples to use “async def” rather than generators. There is a third example that still uses generators which I did not change, because I would also have to update the sequence diagram.
msg245737 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-24 13:11
Thanks for reviewing this Yury. Here is a new patch:

* Drop the “native” term; distinguish by referring to “async def” where necessary
* Add generator version of display_date() coroutine example
* Change chained coroutine example over to “async def” and updated sequence diagram
* Change one other example over (then I ran into Issue 24495)
* Include Yury’s new documentation for @types.coroutine from Issue 24325
msg245751 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015-06-24 15:04
New changeset e31aad001fdb by Yury Selivanov in branch '3.5':
Issue #24439: Improve PEP 492 related docs.
https://hg.python.org/cpython/rev/e31aad001fdb

New changeset d99770da3b2a by Yury Selivanov in branch 'default':
Merge 3.5 (issue #24439)
https://hg.python.org/cpython/rev/d99770da3b2a
msg245752 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-06-24 15:06
Committed. Thanks a lot, Martin!
msg245787 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-06-24 23:38
Hi Yury. It looks like you missed my updated /Doc/library/tulip_coro.dia file. Here it is separately, in case you are having trouble extracting it from the patch.
msg245815 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015-06-25 15:48
New changeset e9c34e16f445 by Yury Selivanov in branch '3.5':
Issue #24439: Update tulip_coro.dia
https://hg.python.org/cpython/rev/e9c34e16f445

New changeset 15b8a62da6ff by Yury Selivanov in branch 'default':
Merge 3.5 (Issue #24439)
https://hg.python.org/cpython/rev/15b8a62da6ff
msg245816 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-06-25 15:51
Martin, it's now updated.

(As for why it wasn't applied in the first place, I guess standard MacOS `patch` command fails to apply git binary diffs)
History
Date User Action Args
2015-06-25 15:51:11yselivanovsetmessages: + msg245816
2015-06-25 15:48:59python-devsetmessages: + msg245815
2015-06-24 23:38:47martin.pantersetfiles: + tulip_coro.dia

messages: + msg245787
2015-06-24 15:06:25yselivanovsetstatus: open -> closed
resolution: fixed
messages: + msg245752

stage: patch review -> resolved
2015-06-24 15:04:57python-devsetnosy: + python-dev
messages: + msg245751
2015-06-24 13:13:43martin.pantersetfiles: + tulip_coro.png
2015-06-24 13:11:50martin.pantersetfiles: + async-doc.v3.patch

messages: + msg245737
2015-06-23 12:26:33martin.pantersetfiles: + async-doc.v2.patch

messages: + msg245680
2015-06-22 18:34:53yselivanovsetnosy: + gvanrossum, vstinner, asvetlov
messages: + msg245642
2015-06-22 12:00:36martin.pantersetmessages: + msg245624
2015-06-22 03:43:18yselivanovsetmessages: + msg245616
2015-06-17 16:52:45yselivanovsetassignee: docs@python -> yselivanov

messages: + msg245442
nosy: + ncoghlan
2015-06-16 03:41:52martin.pantersetfiles: + async-doc.patch
messages: + msg245415

dependencies: + Awaitable ABC incompatible with functools.singledispatch
keywords: + patch
stage: patch review
2015-06-12 14:46:06martin.pantercreate