[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