[issue30831] Inconsistent or wrong documentation around Asynchronous Context Manager

New submission from Daisuke Miyakawa: 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. ---------- assignee: docs@python components: Documentation messages: 297542 nosy: dmiyakawa, docs@python priority: normal severity: normal status: open title: Inconsistent or wrong documentation around Asynchronous Context Manager versions: Python 3.6, Python 3.7 _______________________________________ Python tracker <report@bugs.python.org> <http://bugs.python.org/issue30831> _______________________________________

Change by Andrés Delfino <adelfino@gmail.com>: ---------- nosy: +adelfino, yselivanov versions: +Python 3.8 _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue30831> _______________________________________

Yury Selivanov <yselivanov@gmail.com> added the comment: 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. ---------- resolution: -> not a bug stage: -> resolved status: open -> closed _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue30831> _______________________________________

Yury Selivanov <yselivanov@gmail.com> added the comment: 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. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue30831> _______________________________________
participants (3)
-
Andrés Delfino
-
Daisuke Miyakawa
-
Yury Selivanov