trbabb
trbabb

Reputation: 2095

Await future/coroutine inside event loop; outside coroutine

I have some code with this structure:

def my_callback():
    loop = asyncio.get_event_loop()
    return loop.run_until_complete(my_coroutine_2())

async def my_coroutine_2():
    return await my_stuff.some_future

async def my_coroutine():
    not_my_code.library_function(my_callback)

Inside my_callback, which the library_function calls, I need to access and run coroutine code.

However, run_until_complete produces an error: Event loop already running!.

Is it possible to accomplish what's described above?

That is to say, is there an ordinary function call which will cause the event loop to pause whatever coroutine is currently executing (i.e. the caller of loop.run_until_complete()), and continue executing other coroutines (including the one passed to run_until_complete()) until the requested coroutine is finished, in which case the ordinary function un-blocks?

I've thought that maybe threads could help, but as I understand it, all coroutines have to belong to the same thread.

How do I do this? Is it possible?

Upvotes: 0

Views: 718

Answers (1)

user4815162342
user4815162342

Reputation: 154856

Assuming the external library is thread-safe, you can invoke its function off-thread using run_in_executor. While waiting for the function to finish, the current coroutine will be blocked, but the event loop will keep running.

When the your callback is invoked from another thread, it can use run_coroutine_threadsafe to submit the coroutine to the (still fully functional) event loop, and report the result back to the library:

async def my_coroutine():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, not_my_code.library_function,
                               lambda: my_callback(loop))

def my_callback(loop):
    return asyncio.run_coroutine_threadsafe(my_coroutine_2(), loop)

Upvotes: 1

Related Questions