Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
colesbury merged 6 commits intopython:mainfromcolesbury:gh-120321-gen-state
Feb 3, 2026
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletionsDoc/library/inspect.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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`` |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Please add a.. versionchanged:: next markup at the end of the table to mention that gi_state, ag_state, cr_state are added in Python 3.15.

colesbury reacted with thumbs up emoji
+-----------------+-------------------+---------------------------+
| async generator | __name__ | name |
+-----------------+-------------------+---------------------------+
| | __qualname__ | qualified name |
Expand All@@ -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 |
Expand All@@ -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 |
Expand DownExpand Up@@ -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)``
Expand Down
16 changes: 7 additions & 9 deletionsInclude/internal/pycore_frame.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Don't forget to mention FRAME_COMPLETED removal in the commit message. I'm fine with the removal, the constant was only used in one place: in FRAME_STATE_FINISHED().

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I've edited the PR description and will use that when squash + merging.

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
Expand Down
12 changes: 12 additions & 0 deletionsInclude/internal/pycore_global_objects_fini_generated.h
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

12 changes: 12 additions & 0 deletionsInclude/internal/pycore_global_strings.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand Down
12 changes: 12 additions & 0 deletionsInclude/internal/pycore_runtime_init_generated.h
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

48 changes: 48 additions & 0 deletionsInclude/internal/pycore_unicodeobject_generated.h
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

24 changes: 3 additions & 21 deletionsLib/inspect.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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):
Expand DownExpand Up@@ -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):
Expand DownExpand Up@@ -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):
Expand Down
2 changes: 1 addition & 1 deletionLib/test/test_generators.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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).
Expand Down
6 changes: 6 additions & 0 deletionsLib/types.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -279,6 +279,12 @@ def gi_yieldfrom(self):
@property
defgi_suspended(self):
returnself.__wrapped.gi_suspended
@property
defgi_state(self):
returnself.__wrapped.gi_state
@property
defcr_state(self):
returnself.__wrapped.gi_state.replace('GEN_','CORO_')
cr_code=gi_code
cr_frame=gi_frame
cr_running=gi_running
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -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.
66 changes: 66 additions & 0 deletionsObjects/genobject.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
{
Expand DownExpand Up@@ -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 */
};

Expand DownExpand Up@@ -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")},
Expand All@@ -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 */
};

Expand DownExpand Up@@ -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")},
Expand All@@ -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 */
};

Expand Down
Loading
Loading

[8]ページ先頭

©2009-2026 Movatter.jp