diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2023-03-07 21:27:46 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-07 21:27:46 (GMT) |
commit | a33ca2ad1fcf857817cba505a788e15cf9d6ed0c (patch) | |
tree | 8717635b992632d1f3f95f27631f0085423d5423 | |
parent | 54060ae91da2df44b3f6e6c698694d40284687e9 (diff) | |
download | cpython-a33ca2ad1fcf857817cba505a788e15cf9d6ed0c.zip cpython-a33ca2ad1fcf857817cba505a788e15cf9d6ed0c.tar.gz cpython-a33ca2ad1fcf857817cba505a788e15cf9d6ed0c.tar.bz2 |
gh-102493: fix normalization in PyErr_SetObject (#102502)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
-rw-r--r-- | Lib/test/test_capi/test_exceptions.py | 28 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2023-03-07-16-56-28.gh-issue-102493.gTXrcD.rst | 1 | ||||
-rw-r--r-- | Modules/_testcapi/exceptions.c | 15 | ||||
-rw-r--r-- | Python/errors.c | 16 |
4 files changed, 56 insertions, 4 deletions
diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py index b543a1a..55f1316 100644 --- a/Lib/test/test_capi/test_exceptions.py +++ b/Lib/test/test_capi/test_exceptions.py @@ -140,6 +140,34 @@ class Test_ErrSetAndRestore(unittest.TestCase): self.assertEqual(1, v.args[0]) self.assertIs(tb, v.__traceback__.tb_next) + def test_set_object(self): + + # new exception as obj is not an exception + with self.assertRaises(ValueError) as e: + _testcapi.exc_set_object(ValueError, 42) + self.assertEqual(e.exception.args, (42,)) + + # wraps the exception because unrelated types + with self.assertRaises(ValueError) as e: + _testcapi.exc_set_object(ValueError, TypeError(1,2,3)) + wrapped = e.exception.args[0] + self.assertIsInstance(wrapped, TypeError) + self.assertEqual(wrapped.args, (1, 2, 3)) + + # is superclass, so does not wrap + with self.assertRaises(PermissionError) as e: + _testcapi.exc_set_object(OSError, PermissionError(24)) + self.assertEqual(e.exception.args, (24,)) + + class Meta(type): + def __subclasscheck__(cls, sub): + 1/0 + + class Broken(Exception, metaclass=Meta): + pass + + with self.assertRaises(ZeroDivisionError) as e: + _testcapi.exc_set_object(Broken, Broken()) if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-07-16-56-28.gh-issue-102493.gTXrcD.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-07-16-56-28.gh-issue-102493.gTXrcD.rst new file mode 100644 index 0000000..4c4e88c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-07-16-56-28.gh-issue-102493.gTXrcD.rst @@ -0,0 +1 @@ +Fix regression in semantics of normalisation in ``PyErr_SetObject``. diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c index 43b88cc..a057521 100644 --- a/Modules/_testcapi/exceptions.c +++ b/Modules/_testcapi/exceptions.c @@ -79,6 +79,20 @@ make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs) } static PyObject * +exc_set_object(PyObject *self, PyObject *args) +{ + PyObject *exc; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "OO:exc_set_object", &exc, &obj)) { + return NULL; + } + + PyErr_SetObject(exc, obj); + return NULL; +} + +static PyObject * raise_exception(PyObject *self, PyObject *args) { PyObject *exc; @@ -247,6 +261,7 @@ static PyMethodDef test_methods[] = { PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")}, {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc), METH_VARARGS | METH_KEYWORDS}, + {"exc_set_object", exc_set_object, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, {"set_exc_info", test_set_exc_info, METH_VARARGS}, diff --git a/Python/errors.c b/Python/errors.c index f573bed..bbf6d39 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -149,9 +149,16 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) exception); return; } - Py_XINCREF(value); /* Normalize the exception */ - if (value == NULL || (PyObject *)Py_TYPE(value) != exception) { + int is_subclass = 0; + if (value != NULL && PyExceptionInstance_Check(value)) { + is_subclass = PyObject_IsSubclass((PyObject *)Py_TYPE(value), exception); + if (is_subclass < 0) { + return; + } + } + Py_XINCREF(value); + if (!is_subclass) { /* We must normalize the value right now */ PyObject *fixed_value; @@ -206,9 +213,10 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) Py_DECREF(exc_value); } } - if (value != NULL && PyExceptionInstance_Check(value)) + assert(value != NULL); + if (PyExceptionInstance_Check(value)) tb = PyException_GetTraceback(value); - _PyErr_Restore(tstate, Py_XNewRef(exception), value, tb); + _PyErr_Restore(tstate, Py_NewRef(Py_TYPE(value)), value, tb); } void |