]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-102493: fix normalization in PyErr_SetObject (#102502)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Tue, 7 Mar 2023 21:27:46 +0000 (21:27 +0000)
committerGitHub <noreply@github.com>
Tue, 7 Mar 2023 21:27:46 +0000 (13:27 -0800)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Lib/test/test_capi/test_exceptions.py
Misc/NEWS.d/next/Core and Builtins/2023-03-07-16-56-28.gh-issue-102493.gTXrcD.rst [new file with mode: 0644]
Modules/_testcapi/exceptions.c
Python/errors.c

index b543a1a565a56f5083401b68dc32840f48288548..55f131699a25670b3fd8f2eb6bfb59787ee42b4b 100644 (file)
@@ -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 (file)
index 0000000..4c4e88c
--- /dev/null
@@ -0,0 +1 @@
+Fix regression in semantics of normalisation in ``PyErr_SetObject``.
index 43b88ccf261d989919ae27e9ae558be1cfef9330..a0575213987ffcfb715ed468fd61a8ed118fc73d 100644 (file)
@@ -78,6 +78,20 @@ make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
     return PyErr_NewExceptionWithDoc(name, doc, base, dict);
 }
 
+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)
 {
@@ -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},
index f573bed3d63ef047480004fd71c0562f43b245d9..bbf6d397ce8097c9b7f9ee1ada833527bbd507f9 100644 (file)
@@ -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