Skip to content

Commit bf0394d

Browse files
committed
Add new instruction to make generator or coroutine from current frame.
1 parent c8319f7 commit bf0394d

File tree

12 files changed

+174
-106
lines changed

12 files changed

+174
-106
lines changed

Include/opcode.h

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

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ def _write_atomic(path, data, mode=0o666):
380380
# Python 3.11a4 3472 (bpo-46009: replace GEN_START with POP_TOP)
381381
# Python 3.11a4 3473 (Add POP_JUMP_IF_NOT_NONE/POP_JUMP_IF_NONE opcodes)
382382
# Python 3.11a4 3474 (Add RESUME opcode)
383+
# Python 3.11a5 3475 (Add RETURN_GENERATOR opcode)
383384

384385
# Python 3.12 will start with magic number 3500
385386

@@ -393,7 +394,7 @@ def _write_atomic(path, data, mode=0o666):
393394
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
394395
# in PC/launcher.c must also be updated.
395396

396-
MAGIC_NUMBER = (3474).to_bytes(2, 'little') + b'\r\n'
397+
MAGIC_NUMBER = (3476).to_bytes(2, 'little') + b'\r\n'
397398
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
398399

399400
_PYCACHE = '__pycache__'

Lib/inspect.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,11 +1819,11 @@ def getgeneratorstate(generator):
18191819
"""
18201820
if generator.gi_running:
18211821
return GEN_RUNNING
1822+
if generator.gi_suspended:
1823+
return GEN_SUSPENDED
18221824
if generator.gi_frame is None:
18231825
return GEN_CLOSED
1824-
if generator.gi_frame.f_lasti == -1:
1825-
return GEN_CREATED
1826-
return GEN_SUSPENDED
1826+
return GEN_CREATED
18271827

18281828

18291829
def getgeneratorlocals(generator):
@@ -1861,11 +1861,11 @@ def getcoroutinestate(coroutine):
18611861
"""
18621862
if coroutine.cr_running:
18631863
return CORO_RUNNING
1864+
if coroutine.cr_suspended:
1865+
return CORO_SUSPENDED
18641866
if coroutine.cr_frame is None:
18651867
return CORO_CLOSED
1866-
if coroutine.cr_frame.f_lasti == -1:
1867-
return CORO_CREATED
1868-
return CORO_SUSPENDED
1868+
return CORO_CREATED
18691869

18701870

18711871
def getcoroutinelocals(coroutine):

Lib/opcode.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def jabs_op(name, op):
9494

9595
def_op('GET_AWAITABLE', 73)
9696
def_op('LOAD_ASSERTION_ERROR', 74)
97+
def_op('RETURN_GENERATOR', 75)
9798

9899
def_op('LIST_TO_TUPLE', 82)
99100
def_op('RETURN_VALUE', 83)
@@ -152,10 +153,9 @@ def jabs_op(name, op):
152153
jabs_op('POP_JUMP_IF_NOT_NONE', 128)
153154
jabs_op('POP_JUMP_IF_NONE', 129)
154155
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
155-
156156
def_op('MAKE_FUNCTION', 132) # Flags
157157
def_op('BUILD_SLICE', 133) # Number of items
158-
158+
jabs_op('YIELD_FROM_LOOP', 134) # Target byte offset from beginning of code
159159
def_op('MAKE_CELL', 135)
160160
hasfree.append(135)
161161
def_op('LOAD_CLOSURE', 136)

Lib/test/test_asyncio/test_tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ async def runner():
322322
self.loop.run_until_complete(runner())
323323

324324
def test_task_repr(self):
325+
self.maxDiff = None
325326
self.loop.set_debug(False)
326327

327328
async def notmuch():

Lib/test/test_generators.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def generator1(self):
2828

2929
def generator2(self):
3030
try:
31-
yield
31+
while True:
32+
yield
3233
except KeyboardInterrupt:
3334
return "PASSED"
3435
else:
@@ -897,7 +898,7 @@ def b():
897898
>>> type(i)
898899
<class 'generator'>
899900
>>> [s for s in dir(i) if not s.startswith('_')]
900-
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
901+
['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
901902
>>> from test.support import HAVE_DOCSTRINGS
902903
>>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
903904
Implement next(self).

Objects/frameobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ mark_stacks(PyCodeObject *code_obj, int len)
242242
break;
243243
}
244244
case JUMP_ABSOLUTE:
245+
case YIELD_FROM_LOOP:
245246
j = get_arg(code, i);
246247
assert(j < len);
247248
if (stacks[j] == UNINITIALIZED && j < i) {

Objects/genobject.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ _PyGen_Finalize(PyObject *self)
8787
issue a RuntimeWarning. */
8888
if (gen->gi_code != NULL &&
8989
((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
90-
((InterpreterFrame *)gen->gi_iframe)->f_lasti == -1)
90+
((InterpreterFrame *)gen->gi_iframe)->f_state == FRAME_CREATED)
9191
{
9292
_PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
9393
}
@@ -156,7 +156,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
156156
PyObject *result;
157157

158158
*presult = NULL;
159-
if (frame->f_lasti < 0 && arg && arg != Py_None) {
159+
if (frame->f_state == FRAME_CREATED && arg && arg != Py_None) {
160160
const char *msg = "can't send non-None value to a "
161161
"just-started generator";
162162
if (PyCoro_CheckExact(gen)) {
@@ -753,6 +753,15 @@ gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
753753
return PyBool_FromLong(_PyFrame_IsExecuting((InterpreterFrame *)gen->gi_iframe));
754754
}
755755

756+
static PyObject *
757+
gen_getsuspended(PyGenObject *gen, void *Py_UNUSED(ignored))
758+
{
759+
if (gen->gi_frame_valid == 0) {
760+
Py_RETURN_FALSE;
761+
}
762+
return PyBool_FromLong(((InterpreterFrame *)gen->gi_iframe)->f_state == FRAME_SUSPENDED);
763+
}
764+
756765
static PyObject *
757766
_gen_getframe(PyGenObject *gen, const char *const name)
758767
{
@@ -780,6 +789,7 @@ static PyGetSetDef gen_getsetlist[] = {
780789
PyDoc_STR("object being iterated by yield from, or None")},
781790
{"gi_running", (getter)gen_getrunning, NULL, NULL},
782791
{"gi_frame", (getter)gen_getframe, NULL, NULL},
792+
{"gi_suspended", (getter)gen_getsuspended, NULL, NULL},
783793
{NULL} /* Sentinel */
784794
};
785795

@@ -901,7 +911,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func)
901911
}
902912

903913
static PyObject *
904-
compute_cr_origin(int origin_depth);
914+
compute_cr_origin(int origin_depth, InterpreterFrame *current_frame);
905915

906916
PyObject *
907917
_Py_MakeCoro(PyFunctionObject *func)
@@ -935,7 +945,8 @@ _Py_MakeCoro(PyFunctionObject *func)
935945
if (origin_depth == 0) {
936946
((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
937947
} else {
938-
PyObject *cr_origin = compute_cr_origin(origin_depth);
948+
assert(_PyEval_GetFrame());
949+
PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()->previous);
939950
((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
940951
if (!cr_origin) {
941952
Py_DECREF(coro);
@@ -1097,6 +1108,15 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
10971108
return yf;
10981109
}
10991110

1111+
static PyObject *
1112+
cr_getsuspended(PyCoroObject *coro, void *Py_UNUSED(ignored))
1113+
{
1114+
if (coro->cr_frame_valid == 0) {
1115+
Py_RETURN_FALSE;
1116+
}
1117+
return PyBool_FromLong(((InterpreterFrame *)coro->cr_iframe)->f_state == FRAME_SUSPENDED);
1118+
}
1119+
11001120
static PyObject *
11011121
cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored))
11021122
{
@@ -1122,6 +1142,7 @@ static PyGetSetDef coro_getsetlist[] = {
11221142
PyDoc_STR("object being awaited on, or None")},
11231143
{"cr_running", (getter)cr_getrunning, NULL, NULL},
11241144
{"cr_frame", (getter)cr_getframe, NULL, NULL},
1145+
{"cr_suspended", (getter)cr_getsuspended, NULL, NULL},
11251146
{NULL} /* Sentinel */
11261147
};
11271148

@@ -1299,9 +1320,9 @@ PyTypeObject _PyCoroWrapper_Type = {
12991320
};
13001321

13011322
static PyObject *
1302-
compute_cr_origin(int origin_depth)
1323+
compute_cr_origin(int origin_depth, InterpreterFrame *current_frame)
13031324
{
1304-
InterpreterFrame *frame = _PyEval_GetFrame();
1325+
InterpreterFrame *frame = current_frame;
13051326
/* First count how many frames we have */
13061327
int frame_count = 0;
13071328
for (; frame && frame_count < origin_depth; ++frame_count) {
@@ -1313,7 +1334,10 @@ compute_cr_origin(int origin_depth)
13131334
if (cr_origin == NULL) {
13141335
return NULL;
13151336
}
1316-
frame = _PyEval_GetFrame();
1337+
frame = current_frame;
1338+
if (frame) {
1339+
1340+
}
13171341
for (int i = 0; i < frame_count; ++i) {
13181342
PyCodeObject *code = frame->f_code;
13191343
PyObject *frameinfo = Py_BuildValue("OiO",
@@ -1345,7 +1369,7 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
13451369
if (origin_depth == 0) {
13461370
((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
13471371
} else {
1348-
PyObject *cr_origin = compute_cr_origin(origin_depth);
1372+
PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame());
13491373
((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
13501374
if (!cr_origin) {
13511375
Py_DECREF(coro);

0 commit comments

Comments
 (0)