Comments inline..
>From types.coroutine (which is also called by asyncio.coroutine)...
211 def coroutine(func):
212 """Convert regular generator function to a coroutine."""
213
214 if not callable(func):
215 raise TypeError('types.coroutine() expects a callable')
216
217 if (func.__class__ is FunctionType and
218 getattr(func, '__code__', None).__class__ is CodeType):
219
220 co_flags = func.__code__.co_flags
221
222 # Check if 'func' is a coroutine function.
223 # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
224 if co_flags & 0x180:
225 return func
226
227 # Check if 'func' is a generator function.
228 # (0x20 == CO_GENERATOR)
229 if co_flags & 0x20:
230 # TODO: Implement this in C.
231 co = func.__code__
232 func.__code__ = CodeType(
233 co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
234 co.co_stacksize,
*235 co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE*
236 co.co_code,
237 co.co_consts, co.co_names, co.co_varnames,
co.co_filename,
238 co.co_name, co.co_firstlineno, co.co_lnotab,
co.co_freevars,
239 co.co_cellvars)
240 return func
Line 235: Setting CO_ITERABLE_COROUTINE but not CO_ITERABLE makes
inspect.awaitable(corofunc()) pass but prevents
inspect.iscoroutinefunction(forofunc) from passing. Setting the flags to
(CO_ITERABLE_COROUTINE & CO_ITERABLE) in this same context actually
produces passing behavior for inspect.iscoroutinefuncion.
In addition, it triggers some other diagnostic code from genobject so you
get some handy messages like, "RuntimeWarning: coroutine 'sleep' was never
awaited".
See (from genobject.c):
62 /* If `gen` is a coroutine, and if it was never awaited on,
63 issue a RuntimeWarning. */
64 if (gen->gi_code != NULL &&
65 ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
66 gen->gi_frame->f_lasti == -1) {
67 if (!error_value) {
68 PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
69 "coroutine '%.50S' was never awaited",
70 gen->gi_qualname);
71 }
72 }
My primary question here is about the intended difference
between CO_ITERABLE_COROUTINE and CO_ITERABLE; How perverse is a change
that naively modifies line 235 of types.coroutine to set both flags? In my
limited testing it produced better behavior than stock, but I honestly
can't figure out the intended differences between those flags.
On Mon, Nov 7, 2016 at 10:58 PM Yury Selivanov <[email protected]>
wrote:
> On 2016-11-08 12:47 AM, Justin Mayfield wrote:
>
> > That's quite surprising to me too, BTW. I was focused on generator vs
> > `async def ...` coros and hadn't even tried the legacy decorator.
> >
> > Would it be possible for asyncio code to be altered in a way that
> produces
> > awaitables which conform with inspect's protocol? Ie. through code
> flags
> > or some other flag that inspect looks for? I assume a similar stance
> could
> > then be taken by other async libraries.
> There is no protocol here. The inspect module is simply tailored for
> introspection of builtin types. If something mimics a builtin type (a
> coroutine, a generator, or a simple function) inspect utils won't
> recognize it.
>
> > If it's just not in the cards may I humbly suggest the docs for the
> inspect
> > module include some language to help users avoid some of these
> > idiosyncrasies?
>
> Yes, I think we can improve docs: better clarify what is a
> coroutine/coroutine function, reference asyncio functions, add an
> example. Feel free to submit a patch!
>
> Yury
>
>