This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Inconsistent or wrong documentation around Asynchronous Context Manager
Type: Stage: resolved
Components: Documentation Versions: Python 3.8, Python 3.7, Python 3.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: docs@python Nosy List: adelfino, dmiyakawa, docs@python, yselivanov
Priority: normal Keywords:

Created on 2017-07-03 08:13 by dmiyakawa, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (3)
msg297542 - (view) Author: Daisuke Miyakawa (dmiyakawa) * Date: 2017-07-03 08:13
I was reading the following doc and got confused.

https://docs.python.org/3.7/reference/datamodel.html#object.__aenter__

According to the API doc itself (and original PEP 492), __aenter__() "is semantically similar to the __enter__(), with only difference that it must return an awaitable." __aexit__() has similar sentence that confuses me too.

PEP 492 also implies the (awaitable) object returned from obj.__aenter__() will be awaited immediately. In same manner, the (awaitable) object returned from obj.__aexit__() will be awaited too.

(From PEP 492)
> async with EXPR as VAR:
>     BLOCK
>
> which is semantically equivalent to:
> 
> mgr = (EXPR)
> aexit = type(mgr).__aexit__
> aenter = type(mgr).__aenter__(mgr)
> exc = True
> 
> VAR = await aenter
> try:
>     BLOCK
> except:
>     if not await aexit(mgr, *sys.exc_info()):
>         raise
> else:
>     await aexit(mgr, None, None, None)

On the other hand, actual CPython implementation won't do that; it won't await the returned objects.

Moreover, the example shown in the API doc (AsyncContextManager) does NOT return awaitable as the doc itself suggests, but just await inside __aenter__() (and __aexit__()).

> class AsyncContextManager:
>    async def __aenter__(self):
>        await log('entering context')
>
>    async def __aexit__(self, exc_type, exc, tb):
>        await log('exiting context')

I'm not sure which is true; the doc saying "__aenter__() must return awaitable" is just incorrect, or CPython implementation has a bug. Actual source implies former. _ContextManagerMixin in asyncio.locks does not return awaitable at all, for example.

Anyway, I believe there are inconsistencies around here.
msg337433 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2019-03-07 20:42
There's no inconsistency here and the docs are correct.

If you have a function:

  async def foo():
    pass

Then "foo()" call returns a "coroutine", which is an awaitable.  So

  async def __aenter__():
    ...

always returns an awaitable (regardless if there's a return statement or not).


> On the other hand, actual CPython implementation won't do that; it won't await the returned objects.

If always does await the returned object.
msg337435 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2019-03-07 20:46
I also recommend reading this page https://docs.python.org/3/library/asyncio-task.html to get a better grasp on coroutines and how they are evaluated.
History
Date User Action Args
2022-04-11 14:58:48adminsetgithub: 75014
2019-03-07 20:46:30yselivanovsetmessages: + msg337435
2019-03-07 20:42:44yselivanovsetstatus: open -> closed
resolution: not a bug
messages: + msg337433

stage: resolved
2019-03-07 20:34:00adelfinosetnosy: + yselivanov, adelfino

versions: + Python 3.8
2017-07-03 08:13:39dmiyakawacreate