https://github.com/python/cpython/commit/7e2c9bdc989a2e10e093a18287a107aac7750091
commit: 7e2c9bdc989a2e10e093a18287a107aac7750091
branch: main
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2026-02-03T13:06:32-05:00
summary:

gh-120321: Add gi_state, cr_state, and ag_state attributes (gh-144409)

Add `gi_state`, `cr_state`, and `ag_state` attributes to generators,
coroutines, and async generators respectively. These attributes return the
current state as a string (e.g., `GEN_RUNNING`, `CORO_SUSPENDED`).

The `inspect.getgeneratorstate()`, `inspect.getcoroutinestate()`, and
`inspect.getasyncgenstate()` functions now return these attributes directly.

This is in preparation for making `gi_frame` thread-safe, which may involve
stop-the-world synchronization. The new state attributes avoid potential
performance cliffs in `inspect.getgeneratorstate()` and similar functions by
not requiring frame access.

Also removes unused `FRAME_COMPLETED` state and renumbers the frame state enum
to start at 0 instead of -1.

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-02-02-17-50-14.gh-issue-120321.Xfr7tL.rst
M Doc/library/inspect.rst
M Include/internal/pycore_frame.h
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/inspect.py
M Lib/test/test_generators.py
M Lib/types.py
M Objects/genobject.c
M Tools/c-analyzer/cpython/ignored.tsv

diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 5133f9f0c8e43b..f6bc904bdab4bd 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -262,6 +262,12 @@ attributes (see :ref:`import-mod-attrs` for module 
attributes):
 |                 |                   | ``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            |
@@ -278,6 +284,13 @@ attributes (see :ref:`import-mod-attrs` for module 
attributes):
 +-----------------+-------------------+---------------------------+
 |                 | 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            |
@@ -298,6 +311,12 @@ attributes (see :ref:`import-mod-attrs` for module 
attributes):
 |                 |                   | 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     |
@@ -341,6 +360,11 @@ attributes (see :ref:`import-mod-attrs` for module 
attributes):
 
    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)``
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 50908f2cb7a1d2..5e73ae3c8549b8 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -44,18 +44,16 @@ extern PyFrameObject* _PyFrame_New_NoTrack(PyCodeObject 
*code);
 /* 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
diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index fc297a2933a786..cb62d9424a86b0 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -1335,11 +1335,23 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState 
*interp) {
     _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));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 563ccd7cf6d3f4..92de92079379ea 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -58,11 +58,23 @@ struct _Py_global_strings {
     } 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)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index ba7c0e68434517..dc05495e20d69d 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -1333,11 +1333,23 @@ extern "C" {
 }
 
 #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), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index 44063794293990..10085149f09b6c 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -12,10 +12,42 @@ extern "C" {
 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));
@@ -32,6 +64,22 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
     _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));
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 0dba3c6628c6e5..0eed68d17c702b 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -1813,13 +1813,7 @@ def getgeneratorstate(generator):
       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):
@@ -1855,13 +1849,7 @@ def getcoroutinestate(coroutine):
       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):
@@ -1894,13 +1882,7 @@ def getasyncgenstate(agen):
       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):
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index c181da2e349915..6eb25960101a9e 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -1366,7 +1366,7 @@ def b():
 >>> 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).
diff --git a/Lib/types.py b/Lib/types.py
index 73a69c40c8d4b8..99f23c3f44270f 100644
--- a/Lib/types.py
+++ b/Lib/types.py
@@ -279,6 +279,12 @@ def gi_yieldfrom(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
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-02-17-50-14.gh-issue-120321.Xfr7tL.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-02-17-50-14.gh-issue-120321.Xfr7tL.rst
new file mode 100644
index 00000000000000..3e868c837839e2
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-02-17-50-14.gh-issue-120321.Xfr7tL.rst
@@ -0,0 +1,5 @@
+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.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 5ff4618255c852..5088500fc4142b 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -926,6 +926,26 @@ gen_getsuspended(PyObject *self, void *Py_UNUSED(ignored))
     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)
 {
@@ -974,6 +994,8 @@ static PyGetSetDef gen_getsetlist[] = {
     {"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 */
 };
 
@@ -1291,6 +1313,26 @@ cr_getcode(PyObject *coro, void *Py_UNUSED(ignored))
     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")},
@@ -1302,6 +1344,8 @@ static PyGetSetDef coro_getsetlist[] = {
     {"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 */
 };
 
@@ -1717,6 +1761,26 @@ ag_getcode(PyObject *gen, void *Py_UNUSED(ignored))
     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")},
@@ -1727,6 +1791,8 @@ static PyGetSetDef async_gen_getsetlist[] = {
      {"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 */
 };
 
diff --git a/Tools/c-analyzer/cpython/ignored.tsv 
b/Tools/c-analyzer/cpython/ignored.tsv
index 6ad3fc5f76e57a..91bbf94990ecc1 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -332,6 +332,9 @@ Objects/complexobject.c     -       c_1     -
 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   -

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to