[Python-ideas] PEP 525: Asynchronous Generators
Moritz Sichert
moritz.sichert at googlemail.com
Tue Aug 16 12:46:46 EDT 2016
>>> 2. It's extremely unlikely that somebody will design a system that
>>> switches coroutine runners *while async/awaiting a coroutine*.
>> Yes, I guess so.
>>
>>
>>> But even in this unlikely use case, you can
>>> easily stack finalizers following this pattern:
>>>
>>> old_finalizer = sys.get_asyncgen_finalizer()
>>> sys.set_asyncgen_finalizer(my_finalizer)
>>> try:
>>> # do my thing
>>> finally:
>>> sys.set_asyncgen_finalizer(old_finalizer)
>> That only works for synchronous code, though, because if this is done in a
>> coroutine, it might get suspended within the try block and would leak its
>> own finalizer into the outer world.
>
> set_asyncgen_finalizer is designed to be used *only* by coroutine
> runners. This is a low-level API that coroutines should never
> touch. (At least my experience working with coroutines says so...)
First of all, thanks for your work in this PEP! I think it really completes the
async Python to a state where most synchronous code can be changed easily to be
asynchronous.
I'm not entirely sure about get/set_asyncgen_finalizer(), though. I've written a
short function that converts an async iterator (that includes async generators)
into a normal one:
def sync_iter(async_iter, *, loop=None):
async_iter = async_iter.__aiter__()
is_asyncgen = inspect.isasyncgen(async_iter)
if loop is None:
loop = asyncio.get_event_loop()
if is_asyncgen:
def finalizer(gen):
loop.run_until_complete(gen.aclose())
old_finalizer = sys.get_asyncgen_finalizer()
sys.set_asyncgen_finalizer(finalizer)
try:
while True:
yield loop.run_until_complete(async_iter.__anext__())
except StopAsyncIteration:
return
finally:
if is_asyncgen:
sys.set_asyncgen_finalizer(old_finalizer)
Now my questions:
- Is it correct do check if the async iterator is actually a generator and only
in that case do the whole get/set_asyncgen_finalizer() thing? As I understand,
the finalizer is not needed for "normal" async iterators, i.e. instances of
classes with a __anext__() method.
- Would it make sense to call sys.set_asyncgen_finalizer(old_finalizer) after
the first call of async_iter.__anext__() instead of only at the end? As I
understand the finalizer is set when the generator is started.
- Is loop.run_until_complete(gen.aclose()) a sensible finalizer? If so, is there
even any other finalizer that would make sense? Maybe creating a task for
gen.aclose() instead of waiting for it to be completed?
More information about the Python-ideas
mailing list