cython: asyncio.iscoroutinefunction returns false for Cython async function objects
Referring to #2092
@scoder the implementation in asyncio is ridiculous
def iscoroutinefunction(func):
"""Return True if func is a decorated coroutine function."""
return (inspect.iscoroutinefunction(func) or
getattr(func, '_is_coroutine', None) is _is_coroutine)
So once we agreed inspect returns False, it happens to be that CPython just add a simple attribute to the function _is_coroutine, I think if Cython does this adding up this attribute it will make everything work
So the solution as it can’t be another it results in this, from my test in iPython:
In [46]: fut = init_connection()
In [47]: asyncio.iscoroutine(fut)
Out[47]: True
In [49]: init_connection.__class__
Out[49]: cython_function_or_method
In [50]: asyncio.iscoroutinefunction(init_connection)
Out[50]: False
In [56]: from asyncio.coroutines import _is_coroutine
In [57]: init_connection._is_coroutine = _is_coroutine
In [58]: asyncio.iscoroutinefunction(init_connection)
Out[58]: True
Dirty but easy. Don’t know if this is a 5 seconds issue or a hara-kiri, theoretically it should be easy to plug the attribute runtime from from asyncio.coroutines import _is_coroutine, or not.
This is a super annoying problem (I don’t blame anyone with the word bug) that make cythonized code to be very dramatic in several asyncio frameworks. asyncio.iscoroutine work well with cythonized funcs for the silliest reason I think.
Following the cyfunctions above, create_token is NOT a coroutine, while init_connection is an async function, this workaround works in both so it’s ok if you can schedule the functions whether are coros or not, no way to easily fix this with asyncio.iscoroutinefunction or at least easier than that:
In [59]: async def test():
...: await create_token({})
...:
In [61]: asyncio.get_event_loop().run_until_complete(test())
TypeError: object str can't be used in 'await' expression
In [73]: async def test():
...: return await init_connection()
Out[74]: <asyncpg.pool.Pool at 0x7f78e04752c8>
Here comes the magic:
In [69]: async def test():
...: return await asyncio.coroutine(create_token)({})
In [70]: asyncio.get_event_loop().run_until_complete(test())
Out[70]: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJleHAiOjE1MjU1NTk0NTB9.v8wRZGIke6RYizIpJhl4oaypKyvuVARKaiq0KdpJg6XJ0qTB0o76BuTLera6kSQ_5qXnAb7_9DQSadwdRqgPmw'
In [71]: async def test():
...: return await asyncio.coroutine(init_connection)()
In [72]: asyncio.get_event_loop().run_until_complete(test())
Out[72]: <asyncpg.pool.Pool at 0x7f78df48af48>
With that the workaround:
def nasty_iscoroutinefunction(func):
is_coro = asyncio. iscoroutinefunction(func)
if func.__class__.__name__ == 'cython_function_or_method':
# It's cythonized, danger!
is_coro = True # We'll make it possible
func = asyncio.coroutine(func)
return is_coro
This works!
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 32 (13 by maintainers)
Commits related to this issue
- Make asyncio.iscoroutinefunction() recognise Cython compiled coroutines. (GH-3427) Python's asyncio.coroutines uses an object to tag objects as coroutine functions. We now read this object and use it... — committed to cython/cython by daluz 4 years ago
- update (#1) * Fix test in Py2. * Check for exceptions also when @returns() is used, not only for "->" return type annotations. (GH-3664) When you use Python type annotations, it would be weird ... — committed to sairam4123/cython by sairam4123 4 years ago
I don’t really care that a regular function may return an awaitable, I care about compatibility (or a possibility of compatibility) with CPython. For CPython,
iscoroutinefunctionreturns true if the function was defined with async. As far as I can tell, Cython does not expose this information in any way.There may be reasons for
object.__code__.co_flags & CO_COROUTINEto not be true for async functions in Cython - but at a minimum there must be a programmatic way to determine that a function was defined with async.Repeating my comment above here, so that people know what the status of this ticket is: I’ll accept a PR that adds an _is_coroutine flag to CyFunction and sets it for async functions.
I also agree with the proposal in https://bugs.python.org/issue38225 that there should be an actual protocol for this.
Found an interesting, better and working workaround:
https://github.com/timothycrosley/hug/blob/develop/hug/introspect.py#L33