Skip to content

Commit 69806b9

Browse files
authored
bpo-46009: Do not exhaust generator when send() method raises (GH-29986)
1 parent 3e0f13b commit 69806b9

File tree

5 files changed

+30
-21
lines changed

5 files changed

+30
-21
lines changed

Doc/library/dis.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,9 +1170,8 @@ All of the following opcodes use their arguments.
11701170

11711171
.. opcode:: GEN_START (kind)
11721172

1173-
Pops TOS. If TOS was not ``None``, raises an exception. The ``kind``
1174-
operand corresponds to the type of generator or coroutine and determines
1175-
the error message. The legal kinds are 0 for generator, 1 for coroutine,
1173+
Pops TOS. The ``kind`` operand corresponds to the type of generator or
1174+
coroutine. The legal kinds are 0 for generator, 1 for coroutine,
11761175
and 2 for async generator.
11771176

11781177
.. versionadded:: 3.10

Lib/test/test_generators.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ def f():
162162
with self.assertRaises((TypeError, pickle.PicklingError)):
163163
pickle.dumps(g, proto)
164164

165+
def test_send_non_none_to_new_gen(self):
166+
def f():
167+
yield 1
168+
g = f()
169+
with self.assertRaises(TypeError):
170+
g.send(0)
171+
self.assertEqual(next(g), 1)
172+
165173

166174
class ExceptionTest(unittest.TestCase):
167175
# Tests for the issue #23353: check that the currently handled exception
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Restore behavior from 3.9 and earlier when sending non-None to newly started
2+
generator. In 3.9 this did not affect the state of the generator. In 3.10.0
3+
and 3.10.1 ``gen_func().send(0)`` is equivalent to
4+
``gen_func().throw(TypeError(...)`` which exhausts the generator. In 3.10.2
5+
onward, the behavior has been reverted to that of 3.9.

Objects/genobject.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
157157
PyObject *result;
158158

159159
*presult = NULL;
160+
if (frame->f_lasti < 0 && arg && arg != Py_None) {
161+
const char *msg = "can't send non-None value to a "
162+
"just-started generator";
163+
if (PyCoro_CheckExact(gen)) {
164+
msg = NON_INIT_CORO_MSG;
165+
}
166+
else if (PyAsyncGen_CheckExact(gen)) {
167+
msg = "can't send non-None value to a "
168+
"just-started async generator";
169+
}
170+
PyErr_SetString(PyExc_TypeError, msg);
171+
return PYGEN_ERROR;
172+
}
160173
if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) {
161174
const char *msg = "generator already executing";
162175
if (PyCoro_CheckExact(gen)) {

Python/ceval.c

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,25 +2714,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
27142714

27152715
TARGET(GEN_START) {
27162716
PyObject *none = POP();
2717+
assert(none == Py_None);
2718+
assert(oparg < 3);
27172719
Py_DECREF(none);
2718-
if (!Py_IsNone(none)) {
2719-
if (oparg > 2) {
2720-
_PyErr_SetString(tstate, PyExc_SystemError,
2721-
"Illegal kind for GEN_START");
2722-
}
2723-
else {
2724-
static const char *gen_kind[3] = {
2725-
"generator",
2726-
"coroutine",
2727-
"async generator"
2728-
};
2729-
_PyErr_Format(tstate, PyExc_TypeError,
2730-
"can't send non-None value to a "
2731-
"just-started %s",
2732-
gen_kind[oparg]);
2733-
}
2734-
goto error;
2735-
}
27362720
DISPATCH();
27372721
}
27382722

0 commit comments

Comments
 (0)