question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

asyncio.iscoroutinefunction returns false for Cython async function objects

See original GitHub issue

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

Here: 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:

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!

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:32 (12 by maintainers)

github_iconTop GitHub Comments

6reactions
dhiltonpcommented, Sep 15, 2019

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, iscoroutinefunction returns 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_COROUTINE to 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.

1reaction
scodercommented, Dec 24, 2019

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Issue 38225: iscoroutinefunction broken with cython
If a python piece of code imports cython code with async defs, `asyncio.iscoroutinefunction` cannot determine that the code is async.
Read more >
asyncio.iscoroutinefunction returns False for asynchronous ...
There is no type hierarchy relationship between async functions and async generator functions. Also, the only reason the asyncio.iscoroutine*() ...
Read more >
inspect.iscoroutine only works for "native" coros - Google Groups
iscoroutinefunction says "Return true if the object is a coroutine function. Coroutine functions are defined with "async def" syntax, or generators decorated ...
Read more >
Source code for falcon.asgi.app
import asyncio from inspect import isasyncgenfunction from inspect import ... iscoroutinefunction() always returns False # for cythonized functions.
Read more >
Fast Async Code with Cython and AsyncIO - TIB AV-Portal
Stefan Behnel - Fast Async Code with Cython and AsyncIO Learn how to use the new async/await language feature to write asynchronous code...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found