TLDR
If you want to check something should be used with await
, use inspect.isawaitable
(as when you test something being callable()
other than just being a function).
Unlike iscoroutine
or iscoroutinefunction
it will also work for Future
s and objects that implement __await__
method.
Detailed
Solutions above will work for simple cases, when you pass coroutine function. In some cases you may like to pass awaitable object function that acts like coroutine function, but is not coroutine function. Two examples is Future class or Future-like object class (class that implements __await__
magic method). In this cases iscoroutinefunction
will return False
, what is not you need.
It's easier to understand on non-async example with passing non-function callable as callback:
class SmartCallback:
def __init__(self):
print('SmartCallback is not function, but can be used as function')
callCallback(SmartCallback) # Should work, right?
Back to async world, a similar situation:
class AsyncSmartCallback:
def __await__(self):
return self._coro().__await__()
async def _coro(self):
print('AsyncSmartCallback is not coroutine function, but can be used as coroutine function')
await asyncio.sleep(1)
await callCallback(AsyncSmartCallback) # Should work, but oops! iscoroutinefunction(AsyncSmartCallback) == False
Way to solve it not to use iscoroutine
or iscoroutinefunction
, but use inspect.isawaitable
instead. It works with ready object so you must create it first. In other words, solution I would advise to use:
async def callCallback(cb, arg):
if callable(cb):
res = cb() # here's result of regular func or awaitable
if inspect.isawaitable(res):
res = await res # await if awaitable
return res # return final result
else:
raise ValueError('cb is not callable')
It's more universal (and I'm sure logically correct) solution.