Justin,
First, thanks a lot for a detailed review of PEP 492/inspect/asyncio docs.
> On Nov 8, 2016, at 4:39 AM, Justin Mayfield <
too...@gmail.com> wrote:
>
> Reading into these flags more, I wonder if modifications to the inspect.iscoroutine* functions as well as to genobject.c's finalizer are justified. Specifically to check for both CO_ITERABLE_COROUTINE and CO_COROUTINE in any place where a coroutine is expected. Note that this is an inversion of my last email's statements about setting both flags in types.coroutine. Here is some discovery to help in the discussion...
>
> The "New Coroutine Declaration Syntax" section of PEP 482 states, "Internally, two new code object flags were introduced: CO_COROUTINE is used to mark native coroutines (defined with new syntax). CO_ITERABLE_COROUTINE is used to make generator-based coroutines compatible with native coroutines (set by types.coroutine() function)." Along with, "When a coroutine is garbage collected, a RuntimeWarning is raised if it was never awaited on (see also Debugging Features ).”
This is a bug in the PEP, I’ve pushed an update:
https://git.io/vXuEQ
>
> The last comment does not delineate CO_ITERABLE_COROUTINE from CO_COROUTINE, which makes me think that the genobject.c finalizer code should check for un-awaited coroutines of both types. There are a few other places affected by this including the repr output of objects as seen by before and after repr() calls to asyncio.sleep from my previous experiment in setting both COROUTINE flags...
>
> #stock behavior..
> >>> repr(asyncio.sleep(1))
> '<generator object sleep at 0x7fa5ac7ee0a0>'
>
> # hacked behavior setting both CO_ITERABLE_COROUTINE and CO_COROUTINE...
> >>> asyncio.sleep(1)
> __main__:1: RuntimeWarning: coroutine 'sleep' was never awaited
> <coroutine object sleep at 0x7f8aac4c6360>
>
> I posit that this last behavior is desirable but should be accomplished by finding all places that check for CO_COROUTINE and have them check for CO_ITERABLE_COROUTINE too.
I don’t think we should do that. CO_COROUTINE denotes that the code object will return a new distinct type - PyCoro_Type (as defined in genobject.h).
CO_ITERABLE_COROUTINE can’t exist on its own, it’s a modifier for CO_GENERATOR. Code objects with CO_ITERABLE_COROUTINE will still return *generators*, with only one extra feature: they can be awaited. We decided that we don’t want to change generators semantics too much, after all we have CoroWrapper & debug mode in asyncio to catch unawaited coroutines. We even use CoroWrapper for native coroutines, because it provides you with a more detailed stack trace.
>
> Lastly, the docstring from inspect.iscoroutinefunction says "Return true if the object is a coroutine function. Coroutine functions are defined with "async def" syntax, or generators decorated with `types.coroutine`." What I find problematic here is that types.coroutine never produces a function that is compatible with inspect.iscoroutinefunction.
This was a bug in the docstring, updated now:
https://hg.python.org/cpython/rev/f7f89a4e047d. The code was actually correct, inspect.iscoroutinefunction was checking for only CO_COROUTINE flag.
>
> My suggestion is that we use a mask of something like CO_COROUTINE_ANY defined as (CO_ITERABLE_COROUTINE & CO_COROUTINE), which can be used anywhere coroutine tests need to be performed.
The flag won’t help you with Cython async-def coroutines. They implement collections.abc.Coroutine but they don’t have code objects.
I think I see your confusion now. For determining if something is an asyncio compatible coroutine you should use asyncio.iscoroutine (which is compatible with all kinds of coroutines, including ones compiled with Cython). If you want to check if an object is a Python native type - use inspect.* functions. This has to be better clarified in the docs, I agree.
Yury