How to call async lib function from sync library?

Hey folks,

Similar to: Calling coroutines from sync code

I have a sync library running from a sync main, that has to call a few async coroutines. Optimally, I don’t want to even really create a asyncio loop, I just want to call the coroutines as if they were regular functions, and just run them to completion.

Is this possible?

I’ve tried:

Using asyncio.run(<coroutine>) I get something like:

...
RuntimeError: no running event loop

During handling of the above exception, another exception occurred:

...

File C:\Python311\Lib\site-packages\aiohttp\helpers.py:660, in TimeoutHandle.start(self)
    658     if timeout >= self._ceil_threshold:
    659         when = ceil(when)
--> 660     return self._loop.call_at(when, self.__call__)
    661 else:
    662     return None

File C:\Python311\Lib\asyncio\base_events.py:737, in BaseEventLoop.call_at(self, when, callback, context, *args)
    735 if when is None:
    736     raise TypeError("when cannot be None")
--> 737 self._check_closed()
    738 if self._debug:
    739     self._check_thread()

File C:\Python311\Lib\asyncio\base_events.py:519, in BaseEventLoop._check_closed(self)
    517 def _check_closed(self):
    518     if self._closed:
--> 519         raise RuntimeError('Event loop is closed')

RuntimeError: Event loop is closed

I’ve tried using the recipe from Calling coroutines from sync code - #2 by srittau… but get a similar error.

What is the ‘most correct’ way to run a given asyncio coroutine synchronously? Should I allocate a loop/run till complete then stop the loop each time?

Note: I don’t want to convert the rest of my code to be async code. There must be a simple way to just use async functions (as if they are sync) from sync functions.

That depends on a few factors, so there’s not an unconditional “most correct” way. It almost seems like the most correct way here would be to implement a synchronous version of the function and not have a function coloring problem to begin with since your application is primarily synchronous by description and you want to run the coroutine synchronously.

asyncio.run should work fine if the intent is a one shot coroutine and no persistent asyncio objects that bind to a loop (asyncio.run recreates a unique event loop per use), the error posted looks more like what I would expect from the recipe you linked, as that recipe requires not only an event loop, but a running one. (asyncio.get_running_loop)

Depending on the specific semantics you want and if all your objects are threadsafe, you might want something like this: async-utils/async_utils/bg_loop.py at main · mikeshardmind/async-utils · GitHub which can be used to run all the coroutines of your program in a background loop, and can be set up to block on them finishing or not depending on intent.

The library I’m using unfortunately only provides an async API :frowning_face:. I’ll double check later, but I believe a similar error happens in both cases.

You may want to use an asyncio.Runner.