diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index ebf8bb7e320982..53d579e723c470 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -270,6 +270,32 @@ def gen(): self.assertEqual(next(g), "done") self.assertEqual(sys.exc_info(), (None, None, None)) + def test_except_throw_bad_exception(self): + class E(Exception): + def __new__(cls, *args, **kwargs): + return cls + + def boring_generator(): + yield + + gen = boring_generator() + + err_msg = 'should have returned an instance of BaseException' + + with self.assertRaisesRegex(TypeError, err_msg): + gen.throw(E) + + self.assertRaises(StopIteration, next, gen) + + def generator(): + with self.assertRaisesRegex(TypeError, err_msg): + yield + + gen = generator() + next(gen) + with self.assertRaises(StopIteration): + gen.throw(E) + def test_stopiteration_error(self): # See also PEP 479. diff --git a/Misc/ACKS b/Misc/ACKS index 97a360c8ecfc3c..f845c1c5a48982 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1300,6 +1300,7 @@ Peter Otten Michael Otteneder Richard Oudkerk Russel Owen +Noah Oxer Joonas Paalasmaa Yaroslav Pankovych Martin Packman diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst b/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst new file mode 100644 index 00000000000000..c3b4e810d658b2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-12-21-14-18-32.bpo-39091.dOexgQ.rst @@ -0,0 +1 @@ +Fix crash when using passing a non-exception to a generator's ``throw()`` method. Patch by Noah Oxer diff --git a/Python/errors.c b/Python/errors.c index 1f84215a136a49..eeb84e83835e9f 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -85,17 +85,29 @@ _PyErr_GetTopmostException(PyThreadState *tstate) } static PyObject* -_PyErr_CreateException(PyObject *exception, PyObject *value) +_PyErr_CreateException(PyObject *exception_type, PyObject *value) { + PyObject *exc; + if (value == NULL || value == Py_None) { - return _PyObject_CallNoArg(exception); + exc = _PyObject_CallNoArg(exception_type); } else if (PyTuple_Check(value)) { - return PyObject_Call(exception, value, NULL); + exc = PyObject_Call(exception_type, value, NULL); } else { - return PyObject_CallOneArg(exception, value); + exc = PyObject_CallOneArg(exception_type, value); + } + + if (exc != NULL && !PyExceptionInstance_Check(exc)) { + PyErr_Format(PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %s", + exception_type, Py_TYPE(exc)->tp_name); + Py_CLEAR(exc); } + + return exc; } void