Repository navigation
types._GeneratorWrapper is no longer consistent with types.GeneratorType/types.CoroutineType #143552
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Jan 8, 2026 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Jan 8, 2026 I would add that the
_GeneratorWrapperimplementation itself is somewhat questionable. If we look at how generator-based coroutine objects behave, we see that they do not implement thetypes.CoroutineTypeinterface (and in a sense, even thecollections.abc.Coroutineinterface), but only make generator iterators awaitable:>>> from inspect import getcoroutinestate, getgeneratorstate, isawaitable >>> from types import coroutine >>> @coroutine ... def coroutine_function(): ... return ... yield # generator definition >>> gencoro = coroutine_function() >>> getgeneratorstate(gencoro) 'GEN_CREATED' >>> getcoroutinestate(gencoro) Traceback (most recent call last): AttributeError: 'generator' object has no attribute 'cr_running'. Did you mean: 'gi_running'? >>> dir(gencoro) ['__class__', '__class_getitem__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw'] >>> hasattr(gencoro, '__await__') False >>> isawaitable(gencoro) True
This difference in behavior for different types of passed callable objects may be unexpected (and is it even correct to say that generator-based coroutines are actually considered coroutine objects, as stated in the documentation?). There are three ways to deal with this:
- Remove the
cr_*properties. - Always use
_GeneratorWrapper. - Make generator-based coroutines a separate type.
The third option has been rejected (see the PR mentioned above). The second option, ironically, may not be very good in terms of performance, especially when
types.coroutine()is used to yield objects to the event loop.
Initially,
_GeneratorWrapperwas added in 13f7723 (see #68504; PEP 492). It did not have thecr_*properties, thus replicating only thetypes.GeneratorTypeinterface (the only difference was having the__await__()method). These properties were added in 5376ba9 (see #68588; PEP 492). This was probably done by mistake.- Remove the
Hi, I’ve opened a PR that fixes this issue by adding the missing coroutine introspection attributes for generator-based coroutines.
@VanshAgarwal24036 Please wait until we decided how to proceed. It's not a trivial matter.
Bug report
Bug description:
types.coroutine()uses_GeneratorWrapperas a wrapper for generator iterators in cases where the callable object is not a user-defined function (but is, for example, a generator function compiled via Cython/Nuitka). However, the implementation has not been updated for 11 years and therefore does not provide certain properties, such as:GeneratorType.gi_suspended(Python ≥3.11, bpo-46409: Make generators in bytecode #30633)CoroutineType.cr_suspended(Python ≥3.11, bpo-46409: Make generators in bytecode #30633)CoroutineType.cr_origin(Python ≥3.7, bpo-32591: Add native coroutine origin tracking #5250)As a result, the objects returned by the new function do not look exactly like
types.GeneratorType/types.CoroutineType, which in particular makes them incompatible with theinspect.getgeneratorstate()andinspect.getcoroutinestate()functions:Interestingly, there was already an attempt to add
*_suspendedin #133373 (see #133372 (comment)), but the PR was closed in favor of removing the TODO comment, and this side issue was apparently overlooked.Related: cython/cython#7448.
CPython versions tested on:
3.11, 3.12, 3.13, 3.14
Operating systems tested on:
Linux
Linked PRs