Not all io bound commands are ported to asynchronous reducing somehow the worthy of asyncio library. In fact, we can interpolate the use of both async and sync functions with relative ease of implementation.

I will try using threading interfaces to do the job, in the end I will force_async on a synchronous function and turn it into async one.

def force_async(fn):
    '''
    turns a sync function to async function using threads
    :param sync function:
    :return async funciton:
    '''
    from concurrent.futures import ThreadPoolExecutor
    import asyncio
    pool = ThreadPoolExecutor()

    def wrapper(*args, **kwargs):
        future = pool.submit(fn, *args, **kwargs)
        return asyncio.wrap_future(future)  # make it awaitable

    return wrapper

Usage:

@force_async
def long_blocking_function():
    import time
    time.sleep(10)
    return True

import asyncio
async def run():
    a = long_blocking_function()
    b = long_blocking_function()
    return await asyncio.gather(a, b) # [True, True] in 10 seconds

The main idea of the trick is to run a sync task in a thread (returning a future object, coroutine.Future) and then turn this future object into a asyncio.Future object. Obivously, this future object will be resolve when the sync function is done, and also is awaitable.

If you’re looking for turning async function into sync function, which is much more obivous, see here