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.

Author ncoghlan
Recipients giampaolo.rodola, gvanrossum, ncoghlan, thehesiod, veky, vstinner, yselivanov
Date 2017-03-01.05:01:54
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1488344515.47.0.52881374131.issue29302@psf.upfronthosting.co.za>
In-reply-to
Content
I quite like the idea of enhancing the existing ExitStack to also handle asynchronous operations rather than having completely independent implementations. However, a separate object may end up being simpler in practice as it allows developers to more explicitly declare their intent of writing async-friendly code.

Sketching out what a combined implementation based on the current ExitStack might look like:

- add suitable `__aenter__` and `__aexit__` implementations
- add `enter_context_async` (async def, called with await)
- add `push_aexit` (normal def, but given function is called with await)
- add `coroutine_callback` (normal def, but given coroutine is called with await)
- add `close_async` (async def, called with await)
- add a new internal state flag `_allow_async`. This would default to True, but be set to False when the synchronous `__enter__` is called. When it's false, attempting to register an async callback would raise RuntimeError. `__enter__` would also need to check any previously register contexts and callbacks, and complain if any of them require the use of `Await`
- keep track of which callbacks should be invoked as synchronous calls and which should be called via await
- update `close` to raise RuntimeError if it's asked to clean up asynchronous resources

That's really quite messy and hard to explain, and makes async code look quite different from the corresponding synchronous code.

The repeated checks of `self._allow_async` and `iscoroutinefunction` would also slow down existing synchronous code for no specific end user benefit.

By contrast, a dedicated AsyncExitStack object can:

- assume everything passed to it is a coroutine or an async context manager by default
- always require close() to be called via await
- either add synchronous variants of the default-to-async methods (`enter_context_sync`, `push_sync`, `callback_sync`), or else make them auto-adapt to handle both synchronous and asynchronous inputs
- rather than using `iscoroutinefunction`, instead always invoke callbacks with await, and wrap synchronous callbacks supplied using the above methods in simple one-shot iterators
History
Date User Action Args
2017-03-01 05:01:55ncoghlansetrecipients: + ncoghlan, gvanrossum, vstinner, giampaolo.rodola, yselivanov, thehesiod, veky
2017-03-01 05:01:55ncoghlansetmessageid: <1488344515.47.0.52881374131.issue29302@psf.upfronthosting.co.za>
2017-03-01 05:01:55ncoghlanlinkissue29302 messages
2017-03-01 05:01:54ncoghlancreate