Python: await the generator end

Question:

Current versions of Python (Dec 2022) still allow using @coroutine decorator and a generation can be as:

import asyncio
asyncify = asyncio.coroutine
data_ready = False # Status of a pipe, just to test

def gen():
    global data_ready

    while not data_ready:
        print("not ready")
        data_ready = True # Just to test
        yield

    return "done"

async def main():
    result = await asyncify(gen)()
    print(result)

loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()

However, new Python versions 3.8+ will deprecate @coroutine decorator (the asyncify function alias), how to wait for (await) generator to end as above?

I tried to use async def as expected by the warning but not working:

import asyncio
asyncify = asyncio.coroutine
data_ready = False # Just to test

async def gen():
    global data_ready

    while not data_ready:
        print("not ready")
        data_ready = True # Just to test
        yield

    yield "done"
    return

async def main():
    # this has error: TypeError: object async_generator can't be used in 'await' expression
    result = await gen()
    print(result)

loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()
Asked By: Dee

||

Answers:

Asynchronous generators inherit asynchronous iterator and are aimed for asynchronous iterations.
You can not directly await them as regular coroutines.
With that in mind, returning to your experimental case and your question "how to wait for (await) generator to end?": to get the final yielded value – perform asynchronous iterations:

import asyncio

data_ready = False # Just to test

async def gen():
    global data_ready
    while not data_ready:
        print("not ready")
        data_ready = True # Just to test
        yield "processing"
    yield "done"
    return


async def main():
    a_gen = gen()
    async for result in a_gen:  # assign to result on each async iteration
        pass
    print('result:', result)

asyncio.run(main())

Prints:

not ready
result: done

Naturally, you can also advance the async generator in steps with anext:

a_gen = gen()
val_1 = await anext(a_gen)

Summing up, follow the guidlines on PEP 525 – Asynchronous Generators and try to not mix old-depreceted things with the actual ones.

Answered By: RomanPerekhrest