You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
iscoroutine returns False for native coroutines in python 3.5+ #2092
That's correct, yes. Cython compiled coroutines do not have byte code, and they are not Python generators. They implement the same protocol, but they are technically not the same thing (in the same way that a list iterator is not a dict iterator, even though both are iterators).
The inspect module should definitely return False. Whether the asyncio module should also return False is debatable. It could be argued that that's a bug in asyncio, but deciding that would require a deep look into the internals of asyncio to figure out the exact semantics that is intended there. Please reopen if you have reason to believe that Cython should do something about this, rather than fixing it in asyncio.
@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.
I found a filthy workaround just in case anyone falls into desperation:
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:
defnasty_iscoroutinefunction(func):
is_coro=asyncio. iscoroutinefunction(func)
iffunc.__class__.__name__=='cython_function_or_method':
# It's cythonized, danger!is_coro=True# We'll make it possiblefunc=asyncio.coroutine(func)
returnis_coro
@graingert looking at the implementation, it still requires us to set f.__code__.co_flags to have a certain value.
We've apparently been reluctant to do this because of the possibility that CPython might try to use the flags to process the code object #2273 (comment)
Note that the fact that unittest.mock.AyncMock uses a fake code object and sets a flag on it does not mean that the same thing will work for us at the C level with a real code object using the CO_COROUTINE flag. However, this should at least work safely in CPython <= 3.10, probably up to 3.11, and from those versions on, the inspect change in python/cpython#94050 would take over (from the PoV of a user) if we ever need to remove the flag again. So I'd deem it ok to generally enable the flag for coroutines in Cython 3.0. Until further notice. :)
Activity
scoder commentedon Feb 10, 2018
That's correct, yes. Cython compiled coroutines do not have byte code, and they are not Python generators. They implement the same protocol, but they are technically not the same thing (in the same way that a list iterator is not a dict iterator, even though both are iterators).
The
inspectmodule should definitely returnFalse. Whether theasynciomodule should also returnFalseis debatable. It could be argued that that's a bug inasyncio, but deciding that would require a deep look into the internals ofasyncioto figure out the exact semantics that is intended there. Please reopen if you have reason to believe that Cython should do something about this, rather than fixing it in asyncio.danigosa commentedon May 5, 2018
@scoder the implementation in asyncio is ridiculous
So once we agreed
inspectreturns 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 workHere: https://github.com/python/cpython/blob/789e359f51d2b27bea01b8c6c3bf090aaedf8839/Lib/asyncio/coroutines.py#L152
And here:
https://github.com/python/cpython/blob/789e359f51d2b27bea01b8c6c3bf090aaedf8839/Lib/asyncio/coroutines.py#L160
So the solution as it can't be another it results in this, from my test in iPython:
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.iscoroutinework well with cythonized funcs for the silliest reason I think.danigosa commentedon May 5, 2018
I found a filthy workaround just in case anyone falls into desperation:
Following the
cyfunctionsabove,create_tokenis NOT a coroutine, whileinit_connectionis anasyncfunction, 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 withasyncio.iscoroutinefunctionor at least easier than that:Here comes the magic:
With that the workaround:
This works!
graingert commentedon Jul 18, 2022
inspect.iscoroutinefunction()usesinspect._signature_is_functionlikeso that duck-type functions like Cython functions and AsyncMock can support itthis was backported to 3.10 and is released in 3.11b4
graingert commentedon Jul 18, 2022
Originally posted by @da-woods in #3849 (comment)
scoder commentedon Jul 19, 2022
Thanks @graingert.
Note that the fact that
unittest.mock.AyncMockuses a fake code object and sets a flag on it does not mean that the same thing will work for us at the C level with a real code object using theCO_COROUTINEflag. However, this should at least work safely in CPython <= 3.10, probably up to 3.11, and from those versions on, theinspectchange in python/cpython#94050 would take over (from the PoV of a user) if we ever need to remove the flag again. So I'd deem it ok to generally enable the flag for coroutines in Cython 3.0. Until further notice. :)scoder commentedon Jul 19, 2022
Given that there was a lot more activity in ticket #2273, I'll reopen that one instead.