Nick Coghlan added the comment:

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

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue29302>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to