How can I wrap a synchronous function in an async coroutine? How can I wrap a synchronous function in an async coroutine? python python

How can I wrap a synchronous function in an async coroutine?


Eventually I found an answer in this thread. The method I was looking for is run_in_executor. This allows a synchronous function to be run asynchronously without blocking an event loop.

In the sleep example I posted above, it might look like this:

import asynciofrom time import sleepasync def sleep_async(loop, delay):    # None uses the default executor (ThreadPoolExecutor)    await loop.run_in_executor(None, sleep, delay)    return 'I slept asynchronously'

Also see the following answer -> How do we call a normal function where a coroutine is expected?


You can use a decorator to wrap the sync version to an async version.

import timefrom functools import wraps, partialdef wrap(func):    @wraps(func)    async def run(*args, loop=None, executor=None, **kwargs):        if loop is None:            loop = asyncio.get_event_loop()        pfunc = partial(func, *args, **kwargs)        return await loop.run_in_executor(executor, pfunc)    return run@wrapdef sleep_async(delay):    time.sleep(delay)    return 'I slept asynchronously'

or use the aioify library

% pip install aioify

then

@aioifydef sleep_async(delay):    pass


The decorator would be useful for this case and run your blocking function in another thread.

import asynciofrom concurrent.futures import ThreadPoolExecutorfrom functools import wraps, partialfrom typing import Unionclass to_async:    def __init__(self, *, executor: Optional[ThreadPoolExecutor]=None):               self.executor =  executor        def __call__(self, blocking):        @wraps(blocking)        async def wrapper(*args, **kwargs):            loop = asyncio.get_event_loop()            if not self.executor:                self.executor = ThreadPoolExecutor()            func = partial(blocking, *args, **kwargs)                    return await loop.run_in_executor(self.executor,func)        return wrapper@to_async(executor=None)def sync(*args, **kwargs):    print(args, kwargs)   asyncio.run(sync("hello", "world", result=True))