2

As stated in this question, we can turn a function into a coroutine using the asyncio.coroutine decorator to turn a function into a coroutine like so:

def hello():
    print('Hello World!')

async_hello = asyncio.coroutine(hello)

However this function is deprecated as of python 3.8 (replaced by async def ...). So how can we do this in 3.8+ without async def ...?

Legorooj
  • 2,646
  • 2
  • 15
  • 35
  • 1
    Can you please add more information about what you are actually trying to achieve - *why* are you trying to turn the function into a coroutine in the first place? If the function is sync, you can always just call it from async code. You gain nothing by changing it into a coroutine because it doesn't await anything, so it never suspends. – user4815162342 Mar 05 '21 at 10:47
  • 1
    @user4815162342 Converting a sync function to async is useful to feed it to other code that expects a coroutine. Whether a function is called or called+``await``'ed is not always controllable from the outside. – MisterMiyagi Mar 05 '21 at 10:59
  • 1
    @user4815162342 exactly what @MisterMiyagi said :P. I have a function which grabs and calls a function from a dictionary, expecting it to be a coroutine. I want to be able to convert one of these functions - `func` - from a syncronous function into a coroutine - `func` needs to be called from syncronous code as well, so I can't make it async. – Legorooj Mar 05 '21 at 11:14
  • 1
    @MisterMiyagi But it should be trivial to create an async wrapper that just invokes the function, such a thing doesn't even a generic solution. The `coroutine` decorator was never meant for that usage, it was meant specifically to convert a _generator_ into an asyncio-compatible coroutine. (I'm kind of surprised that it even works on non-generators...) – user4815162342 Mar 05 '21 at 11:15
  • @user4815162342 Yes, I'm also surprised that ``asyncio.coroutine`` worked in the first place. But seeing how it does and is suggested on SO, I do not find its usage here particularly surprising (not that I would recommend it...). – MisterMiyagi Mar 05 '21 at 11:19

1 Answers1

6

Define a custom coroutine wrapper that merely calls the function:

from functools import wraps

def awaitify(sync_func):
    """Wrap a synchronous callable to allow ``await``'ing it"""
    @wraps(sync_func)
    async def async_func(*args, **kwargs):
        return sync_func(*args, **kwargs)
    return async_func

This can be used to make an arbitrary synchronous function compatible wherever a coroutine is expected.

def hello():
    print('Hello World!')

async_hello = awaitify(hello)
asyncio.run(async_hello())  # Hello World!
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119