mirror of https://github.com/python/cpython
bpo-46009: Do not exhaust generator when send() method raises (GH-29986)
This commit is contained in:
parent
3e0f13b9e4
commit
69806b9516
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -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)) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue