| | | ``yield from``, or |
| | | ``None`` |
+-----------------+-------------------+---------------------------+
+| | gi_state | state of the generator, |
+| | | one of ``GEN_CREATED``, |
+| | | ``GEN_RUNNING``, |
+| | | ``GEN_SUSPENDED``, or |
+| | | ``GEN_CLOSED`` |
++-----------------+-------------------+---------------------------+
| async generator | __name__ | name |
+-----------------+-------------------+---------------------------+
| | __qualname__ | qualified name |
+-----------------+-------------------+---------------------------+
| | ag_code | code |
+-----------------+-------------------+---------------------------+
+| | ag_state | state of the async |
+| | | generator, one of |
+| | | ``AGEN_CREATED``, |
+| | | ``AGEN_RUNNING``, |
+| | | ``AGEN_SUSPENDED``, or |
+| | | ``AGEN_CLOSED`` |
++-----------------+-------------------+---------------------------+
| coroutine | __name__ | name |
+-----------------+-------------------+---------------------------+
| | __qualname__ | qualified name |
| | | created, or ``None``. See |
| | | |coroutine-origin-link| |
+-----------------+-------------------+---------------------------+
+| | cr_state | state of the coroutine, |
+| | | one of ``CORO_CREATED``, |
+| | | ``CORO_RUNNING``, |
+| | | ``CORO_SUSPENDED``, or |
+| | | ``CORO_CLOSED`` |
++-----------------+-------------------+---------------------------+
| builtin | __doc__ | documentation string |
+-----------------+-------------------+---------------------------+
| | __name__ | original name of this |
Add ``f_generator`` attribute to frames.
+.. versionchanged:: next
+
+ Add ``gi_state`` attribute to generators, ``cr_state`` attribute to
+ coroutines, and ``ag_state`` attribute to async generators.
+
.. function:: getmembers(object[, predicate])
Return all the members of an object in a list of ``(name, value)``
/* other API */
typedef enum _framestate {
- FRAME_CREATED = -4,
- FRAME_SUSPENDED = -3,
- FRAME_SUSPENDED_YIELD_FROM = -2,
- FRAME_SUSPENDED_YIELD_FROM_LOCKED = -1,
- FRAME_EXECUTING = 0,
- FRAME_COMPLETED = 1,
- FRAME_CLEARED = 4
+ FRAME_CREATED = 0,
+ FRAME_SUSPENDED = 1,
+ FRAME_SUSPENDED_YIELD_FROM = 2,
+ FRAME_SUSPENDED_YIELD_FROM_LOCKED = 3,
+ FRAME_EXECUTING = 4,
+ FRAME_CLEARED = 5
} PyFrameState;
#define FRAME_STATE_SUSPENDED(S) ((S) >= FRAME_SUSPENDED && (S) <= FRAME_SUSPENDED_YIELD_FROM_LOCKED)
-#define FRAME_STATE_FINISHED(S) ((S) >= FRAME_COMPLETED)
-
+#define FRAME_STATE_FINISHED(S) ((S) == FRAME_CLEARED)
#ifdef __cplusplus
}
#endif
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(str_replace_inf));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(type_params));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(utf_8));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CLOSED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_CREATED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_RUNNING));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(AGEN_SUSPENDED));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CANCELLED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CORO_CLOSED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CORO_CREATED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CORO_RUNNING));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CORO_SUSPENDED));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(Emax));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(Emin));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(FINISHED));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(False));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(GEN_CLOSED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(GEN_CREATED));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(GEN_RUNNING));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(GEN_SUSPENDED));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(JSONDecodeError));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(PENDING));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(Py_Repr));
} literals;
struct {
+ STRUCT_FOR_ID(AGEN_CLOSED)
+ STRUCT_FOR_ID(AGEN_CREATED)
+ STRUCT_FOR_ID(AGEN_RUNNING)
+ STRUCT_FOR_ID(AGEN_SUSPENDED)
STRUCT_FOR_ID(CANCELLED)
+ STRUCT_FOR_ID(CORO_CLOSED)
+ STRUCT_FOR_ID(CORO_CREATED)
+ STRUCT_FOR_ID(CORO_RUNNING)
+ STRUCT_FOR_ID(CORO_SUSPENDED)
STRUCT_FOR_ID(Emax)
STRUCT_FOR_ID(Emin)
STRUCT_FOR_ID(FINISHED)
STRUCT_FOR_ID(False)
+ STRUCT_FOR_ID(GEN_CLOSED)
+ STRUCT_FOR_ID(GEN_CREATED)
+ STRUCT_FOR_ID(GEN_RUNNING)
+ STRUCT_FOR_ID(GEN_SUSPENDED)
STRUCT_FOR_ID(JSONDecodeError)
STRUCT_FOR_ID(PENDING)
STRUCT_FOR_ID(Py_Repr)
}
#define _Py_str_identifiers_INIT { \
+ INIT_ID(AGEN_CLOSED), \
+ INIT_ID(AGEN_CREATED), \
+ INIT_ID(AGEN_RUNNING), \
+ INIT_ID(AGEN_SUSPENDED), \
INIT_ID(CANCELLED), \
+ INIT_ID(CORO_CLOSED), \
+ INIT_ID(CORO_CREATED), \
+ INIT_ID(CORO_RUNNING), \
+ INIT_ID(CORO_SUSPENDED), \
INIT_ID(Emax), \
INIT_ID(Emin), \
INIT_ID(FINISHED), \
INIT_ID(False), \
+ INIT_ID(GEN_CLOSED), \
+ INIT_ID(GEN_CREATED), \
+ INIT_ID(GEN_RUNNING), \
+ INIT_ID(GEN_SUSPENDED), \
INIT_ID(JSONDecodeError), \
INIT_ID(PENDING), \
INIT_ID(Py_Repr), \
static inline void
_PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
PyObject *string;
+ string = &_Py_ID(AGEN_CLOSED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(AGEN_CREATED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(AGEN_RUNNING);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(AGEN_SUSPENDED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(CANCELLED);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(CORO_CLOSED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(CORO_CREATED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(CORO_RUNNING);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(CORO_SUSPENDED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(Emax);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(GEN_CLOSED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(GEN_CREATED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(GEN_RUNNING);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(GEN_SUSPENDED);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(JSONDecodeError);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
GEN_SUSPENDED: Currently suspended at a yield expression.
GEN_CLOSED: Execution has completed.
"""
- if generator.gi_running:
- return GEN_RUNNING
- if generator.gi_suspended:
- return GEN_SUSPENDED
- if generator.gi_frame is None:
- return GEN_CLOSED
- return GEN_CREATED
+ return generator.gi_state
def getgeneratorlocals(generator):
CORO_SUSPENDED: Currently suspended at an await expression.
CORO_CLOSED: Execution has completed.
"""
- if coroutine.cr_running:
- return CORO_RUNNING
- if coroutine.cr_suspended:
- return CORO_SUSPENDED
- if coroutine.cr_frame is None:
- return CORO_CLOSED
- return CORO_CREATED
+ return coroutine.cr_state
def getcoroutinelocals(coroutine):
AGEN_SUSPENDED: Currently suspended at a yield expression.
AGEN_CLOSED: Execution has completed.
"""
- if agen.ag_running:
- return AGEN_RUNNING
- if agen.ag_suspended:
- return AGEN_SUSPENDED
- if agen.ag_frame is None:
- return AGEN_CLOSED
- return AGEN_CREATED
+ return agen.ag_state
def getasyncgenlocals(agen):
>>> type(i)
<class 'generator'>
>>> [s for s in dir(i) if not s.startswith('_')]
-['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
+['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_state', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
>>> from test.support import HAVE_DOCSTRINGS
>>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
Implement next(self).
@property
def gi_suspended(self):
return self.__wrapped.gi_suspended
+ @property
+ def gi_state(self):
+ return self.__wrapped.gi_state
+ @property
+ def cr_state(self):
+ return self.__wrapped.gi_state.replace('GEN_', 'CORO_')
cr_code = gi_code
cr_frame = gi_frame
cr_running = gi_running
--- /dev/null
+Add ``gi_state``, ``cr_state``, and ``ag_state`` attributes to generators,
+coroutines, and async generators that return the current state as a string
+(e.g., ``GEN_RUNNING``). The :mod:`inspect` module functions
+:func:`~inspect.getgeneratorstate`, :func:`~inspect.getcoroutinestate`, and
+:func:`~inspect.getasyncgenstate` now return these attributes directly.
return FRAME_STATE_SUSPENDED(frame_state) ? Py_True : Py_False;
}
+static PyObject *
+gen_getstate(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyGenObject *gen = _PyGen_CAST(self);
+ int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state);
+
+ static PyObject *const state_strings[] = {
+ [FRAME_CREATED] = &_Py_ID(GEN_CREATED),
+ [FRAME_SUSPENDED] = &_Py_ID(GEN_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(GEN_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(GEN_SUSPENDED),
+ [FRAME_EXECUTING] = &_Py_ID(GEN_RUNNING),
+ [FRAME_CLEARED] = &_Py_ID(GEN_CLOSED),
+ };
+
+ assert(frame_state >= 0 &&
+ (size_t)frame_state < Py_ARRAY_LENGTH(state_strings));
+ return state_strings[frame_state];
+}
+
static PyObject *
_gen_getframe(PyGenObject *gen, const char *const name)
{
{"gi_frame", gen_getframe, NULL, NULL},
{"gi_suspended", gen_getsuspended, NULL, NULL},
{"gi_code", gen_getcode, NULL, NULL},
+ {"gi_state", gen_getstate, NULL,
+ PyDoc_STR("state of the generator")},
{NULL} /* Sentinel */
};
return _gen_getcode(_PyGen_CAST(coro), "cr_code");
}
+static PyObject *
+cr_getstate(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyGenObject *gen = _PyGen_CAST(self);
+ int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state);
+
+ static PyObject *const state_strings[] = {
+ [FRAME_CREATED] = &_Py_ID(CORO_CREATED),
+ [FRAME_SUSPENDED] = &_Py_ID(CORO_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(CORO_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(CORO_SUSPENDED),
+ [FRAME_EXECUTING] = &_Py_ID(CORO_RUNNING),
+ [FRAME_CLEARED] = &_Py_ID(CORO_CLOSED),
+ };
+
+ assert(frame_state >= 0 &&
+ (size_t)frame_state < Py_ARRAY_LENGTH(state_strings));
+ return state_strings[frame_state];
+}
+
static PyGetSetDef coro_getsetlist[] = {
{"__name__", gen_get_name, gen_set_name,
PyDoc_STR("name of the coroutine")},
{"cr_frame", cr_getframe, NULL, NULL},
{"cr_code", cr_getcode, NULL, NULL},
{"cr_suspended", gen_getsuspended, NULL, NULL},
+ {"cr_state", cr_getstate, NULL,
+ PyDoc_STR("state of the coroutine")},
{NULL} /* Sentinel */
};
return _gen_getcode((PyGenObject*)gen, "ag_code");
}
+static PyObject *
+ag_getstate(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyGenObject *gen = _PyGen_CAST(self);
+ int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state);
+
+ static PyObject *const state_strings[] = {
+ [FRAME_CREATED] = &_Py_ID(AGEN_CREATED),
+ [FRAME_SUSPENDED] = &_Py_ID(AGEN_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(AGEN_SUSPENDED),
+ [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(AGEN_SUSPENDED),
+ [FRAME_EXECUTING] = &_Py_ID(AGEN_RUNNING),
+ [FRAME_CLEARED] = &_Py_ID(AGEN_CLOSED),
+ };
+
+ assert(frame_state >= 0 &&
+ (size_t)frame_state < Py_ARRAY_LENGTH(state_strings));
+ return state_strings[frame_state];
+}
+
static PyGetSetDef async_gen_getsetlist[] = {
{"__name__", gen_get_name, gen_set_name,
PyDoc_STR("name of the async generator")},
{"ag_frame", ag_getframe, NULL, NULL},
{"ag_code", ag_getcode, NULL, NULL},
{"ag_suspended", gen_getsuspended, NULL, NULL},
+ {"ag_state", ag_getstate, NULL,
+ PyDoc_STR("state of the async generator")},
{NULL} /* Sentinel */
};
Objects/exceptions.c - static_exceptions -
Objects/genobject.c - ASYNC_GEN_IGNORED_EXIT_MSG -
Objects/genobject.c - NON_INIT_CORO_MSG -
+Objects/genobject.c gen_getstate state_strings -
+Objects/genobject.c cr_getstate state_strings -
+Objects/genobject.c ag_getstate state_strings -
Objects/longobject.c - _PyLong_DigitValue -
Objects/longobject.c - PyLong_LAYOUT -
Objects/object.c - _Py_SwappedOp -