Skip to content

Commit f3335d3

Browse files
committed
gh-120321: Add gi_state, cr_state, and ag_state attributes
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.
1 parent 29acc08 commit f3335d3

File tree

12 files changed

+198
-37
lines changed

12 files changed

+198
-37
lines changed

Doc/library/inspect.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,12 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
262262
| | | ``yield from``, or |
263263
| | | ``None`` |
264264
+-----------------+-------------------+---------------------------+
265+
| | gi_state | state of the generator, |
266+
| | | one of ``GEN_CREATED``, |
267+
| | | ``GEN_RUNNING``, |
268+
| | | ``GEN_SUSPENDED``, or |
269+
| | | ``GEN_CLOSED`` |
270+
+-----------------+-------------------+---------------------------+
265271
| async generator | __name__ | name |
266272
+-----------------+-------------------+---------------------------+
267273
| | __qualname__ | qualified name |
@@ -278,6 +284,13 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
278284
+-----------------+-------------------+---------------------------+
279285
| | ag_code | code |
280286
+-----------------+-------------------+---------------------------+
287+
| | ag_state | state of the async |
288+
| | | generator, one of |
289+
| | | ``AGEN_CREATED``, |
290+
| | | ``AGEN_RUNNING``, |
291+
| | | ``AGEN_SUSPENDED``, or |
292+
| | | ``AGEN_CLOSED`` |
293+
+-----------------+-------------------+---------------------------+
281294
| coroutine | __name__ | name |
282295
+-----------------+-------------------+---------------------------+
283296
| | __qualname__ | qualified name |
@@ -298,6 +311,12 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
298311
| | | created, or ``None``. See |
299312
| | | |coroutine-origin-link| |
300313
+-----------------+-------------------+---------------------------+
314+
| | cr_state | state of the coroutine, |
315+
| | | one of ``CORO_CREATED``, |
316+
| | | ``CORO_RUNNING``, |
317+
| | | ``CORO_SUSPENDED``, or |
318+
| | | ``CORO_CLOSED`` |
319+
+-----------------+-------------------+---------------------------+
301320
| builtin | __doc__ | documentation string |
302321
+-----------------+-------------------+---------------------------+
303322
| | __name__ | original name of this |

Include/internal/pycore_frame.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,16 @@ extern PyFrameObject* _PyFrame_New_NoTrack(PyCodeObject *code);
4444
/* other API */
4545

4646
typedef enum _framestate {
47-
FRAME_CREATED = -4,
48-
FRAME_SUSPENDED = -3,
49-
FRAME_SUSPENDED_YIELD_FROM = -2,
50-
FRAME_SUSPENDED_YIELD_FROM_LOCKED = -1,
51-
FRAME_EXECUTING = 0,
52-
FRAME_COMPLETED = 1,
53-
FRAME_CLEARED = 4
47+
FRAME_CREATED = 0,
48+
FRAME_SUSPENDED = 1,
49+
FRAME_SUSPENDED_YIELD_FROM = 2,
50+
FRAME_SUSPENDED_YIELD_FROM_LOCKED = 3,
51+
FRAME_EXECUTING = 4,
52+
FRAME_CLEARED = 5
5453
} PyFrameState;
5554

5655
#define FRAME_STATE_SUSPENDED(S) ((S) >= FRAME_SUSPENDED && (S) <= FRAME_SUSPENDED_YIELD_FROM_LOCKED)
57-
#define FRAME_STATE_FINISHED(S) ((S) >= FRAME_COMPLETED)
58-
56+
#define FRAME_STATE_FINISHED(S) ((S) == FRAME_CLEARED)
5957
#ifdef __cplusplus
6058
}
6159
#endif

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,23 @@ struct _Py_global_strings {
5858
} literals;
5959

6060
struct {
61+
STRUCT_FOR_ID(AGEN_CLOSED)
62+
STRUCT_FOR_ID(AGEN_CREATED)
63+
STRUCT_FOR_ID(AGEN_RUNNING)
64+
STRUCT_FOR_ID(AGEN_SUSPENDED)
6165
STRUCT_FOR_ID(CANCELLED)
66+
STRUCT_FOR_ID(CORO_CLOSED)
67+
STRUCT_FOR_ID(CORO_CREATED)
68+
STRUCT_FOR_ID(CORO_RUNNING)
69+
STRUCT_FOR_ID(CORO_SUSPENDED)
6270
STRUCT_FOR_ID(Emax)
6371
STRUCT_FOR_ID(Emin)
6472
STRUCT_FOR_ID(FINISHED)
6573
STRUCT_FOR_ID(False)
74+
STRUCT_FOR_ID(GEN_CLOSED)
75+
STRUCT_FOR_ID(GEN_CREATED)
76+
STRUCT_FOR_ID(GEN_RUNNING)
77+
STRUCT_FOR_ID(GEN_SUSPENDED)
6678
STRUCT_FOR_ID(JSONDecodeError)
6779
STRUCT_FOR_ID(PENDING)
6880
STRUCT_FOR_ID(Py_Repr)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/inspect.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,13 +1813,7 @@ def getgeneratorstate(generator):
18131813
GEN_SUSPENDED: Currently suspended at a yield expression.
18141814
GEN_CLOSED: Execution has completed.
18151815
"""
1816-
if generator.gi_running:
1817-
return GEN_RUNNING
1818-
if generator.gi_suspended:
1819-
return GEN_SUSPENDED
1820-
if generator.gi_frame is None:
1821-
return GEN_CLOSED
1822-
return GEN_CREATED
1816+
return generator.gi_state
18231817

18241818

18251819
def getgeneratorlocals(generator):
@@ -1855,13 +1849,7 @@ def getcoroutinestate(coroutine):
18551849
CORO_SUSPENDED: Currently suspended at an await expression.
18561850
CORO_CLOSED: Execution has completed.
18571851
"""
1858-
if coroutine.cr_running:
1859-
return CORO_RUNNING
1860-
if coroutine.cr_suspended:
1861-
return CORO_SUSPENDED
1862-
if coroutine.cr_frame is None:
1863-
return CORO_CLOSED
1864-
return CORO_CREATED
1852+
return coroutine.cr_state
18651853

18661854

18671855
def getcoroutinelocals(coroutine):
@@ -1894,13 +1882,7 @@ def getasyncgenstate(agen):
18941882
AGEN_SUSPENDED: Currently suspended at a yield expression.
18951883
AGEN_CLOSED: Execution has completed.
18961884
"""
1897-
if agen.ag_running:
1898-
return AGEN_RUNNING
1899-
if agen.ag_suspended:
1900-
return AGEN_SUSPENDED
1901-
if agen.ag_frame is None:
1902-
return AGEN_CLOSED
1903-
return AGEN_CREATED
1885+
return agen.ag_state
19041886

19051887

19061888
def getasyncgenlocals(agen):

Lib/test/test_generators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,7 @@ def b():
13661366
>>> type(i)
13671367
<class 'generator'>
13681368
>>> [s for s in dir(i) if not s.startswith('_')]
1369-
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
1369+
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_state', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
13701370
>>> from test.support import HAVE_DOCSTRINGS
13711371
>>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
13721372
Implement next(self).

Lib/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,12 @@ def gi_yieldfrom(self):
279279
@property
280280
def gi_suspended(self):
281281
return self.__wrapped.gi_suspended
282+
@property
283+
def gi_state(self):
284+
return self.__wrapped.gi_state
285+
@property
286+
def cr_state(self):
287+
return self.__wrapped.gi_state.replace('GEN_', 'CORO_')
282288
cr_code = gi_code
283289
cr_frame = gi_frame
284290
cr_running = gi_running
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Add :attr:`~generator.gi_state`, :attr:`~coroutine.cr_state`, and
2+
:attr:`~types.AsyncGeneratorType.ag_state` attributes that return the current
3+
state of generators, coroutines, and async generators as a string (e.g.,
4+
``GEN_RUNNING``). The :mod:`inspect` module functions
5+
:func:`~inspect.getgeneratorstate`, :func:`~inspect.getcoroutinestate`, and
6+
:func:`~inspect.getasyncgenstate` now return these attributes directly.

0 commit comments

Comments
 (0)