bpo-46009: Do not exhaust generator when send() method raises (GH-29986)

This commit is contained in:
Mark Shannon 2021-12-08 12:09:26 +00:00 committed by GitHub
parent 3e0f13b9e4
commit 69806b9516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 21 deletions

View File

@ -1170,9 +1170,8 @@ All of the following opcodes use their arguments.
.. opcode:: GEN_START (kind) .. opcode:: GEN_START (kind)
Pops TOS. If TOS was not ``None``, raises an exception. The ``kind`` Pops TOS. The ``kind`` operand corresponds to the type of generator or
operand corresponds to the type of generator or coroutine and determines coroutine. The legal kinds are 0 for generator, 1 for coroutine,
the error message. The legal kinds are 0 for generator, 1 for coroutine,
and 2 for async generator. and 2 for async generator.
.. versionadded:: 3.10 .. versionadded:: 3.10

View File

@ -162,6 +162,14 @@ class GeneratorTest(unittest.TestCase):
with self.assertRaises((TypeError, pickle.PicklingError)): with self.assertRaises((TypeError, pickle.PicklingError)):
pickle.dumps(g, proto) pickle.dumps(g, proto)
def test_send_non_none_to_new_gen(self):
def f():
yield 1
g = f()
with self.assertRaises(TypeError):
g.send(0)
self.assertEqual(next(g), 1)
class ExceptionTest(unittest.TestCase): class ExceptionTest(unittest.TestCase):
# Tests for the issue #23353: check that the currently handled exception # Tests for the issue #23353: check that the currently handled exception

View File

@ -0,0 +1,5 @@
Restore behavior from 3.9 and earlier when sending non-None to newly started
generator. In 3.9 this did not affect the state of the generator. In 3.10.0
and 3.10.1 ``gen_func().send(0)`` is equivalent to
``gen_func().throw(TypeError(...)`` which exhausts the generator. In 3.10.2
onward, the behavior has been reverted to that of 3.9.

View File

@ -157,6 +157,19 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
PyObject *result; PyObject *result;
*presult = NULL; *presult = NULL;
if (frame->f_lasti < 0 && arg && arg != Py_None) {
const char *msg = "can't send non-None value to a "
"just-started generator";
if (PyCoro_CheckExact(gen)) {
msg = NON_INIT_CORO_MSG;
}
else if (PyAsyncGen_CheckExact(gen)) {
msg = "can't send non-None value to a "
"just-started async generator";
}
PyErr_SetString(PyExc_TypeError, msg);
return PYGEN_ERROR;
}
if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) { if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) {
const char *msg = "generator already executing"; const char *msg = "generator already executing";
if (PyCoro_CheckExact(gen)) { if (PyCoro_CheckExact(gen)) {

View File

@ -2714,25 +2714,9 @@ check_eval_breaker:
TARGET(GEN_START) { TARGET(GEN_START) {
PyObject *none = POP(); PyObject *none = POP();
assert(none == Py_None);
assert(oparg < 3);
Py_DECREF(none); Py_DECREF(none);
if (!Py_IsNone(none)) {
if (oparg > 2) {
_PyErr_SetString(tstate, PyExc_SystemError,
"Illegal kind for GEN_START");
}
else {
static const char *gen_kind[3] = {
"generator",
"coroutine",
"async generator"
};
_PyErr_Format(tstate, PyExc_TypeError,
"can't send non-None value to a "
"just-started %s",
gen_kind[oparg]);
}
goto error;
}
DISPATCH(); DISPATCH();
} }