]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93649: Split exception tests from _testcapimodule.c (GH-102173)
authorErlend E. Aasland <erlend.aasland@protonmail.com>
Thu, 23 Feb 2023 15:03:13 +0000 (16:03 +0100)
committerGitHub <noreply@github.com>
Thu, 23 Feb 2023 15:03:13 +0000 (07:03 -0800)
Automerge-Triggered-By: GH:erlend-aasland
Lib/test/test_capi/test_exceptions.py [new file with mode: 0644]
Lib/test/test_capi/test_misc.py
Modules/Setup.stdlib.in
Modules/_testcapi/exceptions.c [new file with mode: 0644]
Modules/_testcapi/parts.h
Modules/_testcapimodule.c
PCbuild/_testcapi.vcxproj
PCbuild/_testcapi.vcxproj.filters

diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py
new file mode 100644 (file)
index 0000000..b543a1a
--- /dev/null
@@ -0,0 +1,145 @@
+import re
+import sys
+import unittest
+
+from test import support
+from test.support import import_helper
+from test.support.script_helper import assert_python_failure
+
+from .test_misc import decode_stderr
+
+# Skip this test if the _testcapi module isn't available.
+_testcapi = import_helper.import_module('_testcapi')
+
+class Test_Exceptions(unittest.TestCase):
+
+    def test_exception(self):
+        raised_exception = ValueError("5")
+        new_exc = TypeError("TEST")
+        try:
+            raise raised_exception
+        except ValueError as e:
+            orig_sys_exception = sys.exception()
+            orig_exception = _testcapi.set_exception(new_exc)
+            new_sys_exception = sys.exception()
+            new_exception = _testcapi.set_exception(orig_exception)
+            reset_sys_exception = sys.exception()
+
+            self.assertEqual(orig_exception, e)
+
+            self.assertEqual(orig_exception, raised_exception)
+            self.assertEqual(orig_sys_exception, orig_exception)
+            self.assertEqual(reset_sys_exception, orig_exception)
+            self.assertEqual(new_exception, new_exc)
+            self.assertEqual(new_sys_exception, new_exception)
+        else:
+            self.fail("Exception not raised")
+
+    def test_exc_info(self):
+        raised_exception = ValueError("5")
+        new_exc = TypeError("TEST")
+        try:
+            raise raised_exception
+        except ValueError as e:
+            tb = e.__traceback__
+            orig_sys_exc_info = sys.exc_info()
+            orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
+            new_sys_exc_info = sys.exc_info()
+            new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
+            reset_sys_exc_info = sys.exc_info()
+
+            self.assertEqual(orig_exc_info[1], e)
+
+            self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
+            self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
+            self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
+            self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
+            self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
+        else:
+            self.assertTrue(False)
+
+
+class Test_FatalError(unittest.TestCase):
+
+    def check_fatal_error(self, code, expected, not_expected=()):
+        with support.SuppressCrashReport():
+            rc, out, err = assert_python_failure('-sSI', '-c', code)
+
+        err = decode_stderr(err)
+        self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n',
+                      err)
+
+        match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
+                          err, re.MULTILINE)
+        if not match:
+            self.fail(f"Cannot find 'Extension modules:' in {err!r}")
+        modules = set(match.group(1).strip().split(', '))
+        total = int(match.group(2))
+
+        for name in expected:
+            self.assertIn(name, modules)
+        for name in not_expected:
+            self.assertNotIn(name, modules)
+        self.assertEqual(len(modules), total)
+
+    @support.requires_subprocess()
+    def test_fatal_error(self):
+        # By default, stdlib extension modules are ignored,
+        # but not test modules.
+        expected = ('_testcapi',)
+        not_expected = ('sys',)
+        code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
+        self.check_fatal_error(code, expected, not_expected)
+
+        # Mark _testcapi as stdlib module, but not sys
+        expected = ('sys',)
+        not_expected = ('_testcapi',)
+        code = """if True:
+            import _testcapi, sys
+            sys.stdlib_module_names = frozenset({"_testcapi"})
+            _testcapi.fatal_error(b"MESSAGE")
+        """
+        self.check_fatal_error(code, expected)
+
+
+class Test_ErrSetAndRestore(unittest.TestCase):
+
+    def test_err_set_raised(self):
+        with self.assertRaises(ValueError):
+            _testcapi.err_set_raised(ValueError())
+        v = ValueError()
+        try:
+            _testcapi.err_set_raised(v)
+        except ValueError as ex:
+            self.assertIs(v, ex)
+
+    def test_err_restore(self):
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1, None)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, ValueError())
+        try:
+            _testcapi.err_restore(KeyError, "hi")
+        except KeyError as k:
+            self.assertEqual("hi", k.args[0])
+        try:
+            1/0
+        except Exception as e:
+            tb = e.__traceback__
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1, tb)
+        with self.assertRaises(TypeError):
+            _testcapi.err_restore(ValueError, 1, 0)
+        try:
+            _testcapi.err_restore(ValueError, 1, tb)
+        except ValueError as v:
+            self.assertEqual(1, v.args[0])
+            self.assertIs(tb, v.__traceback__.tb_next)
+
+
+if __name__ == "__main__":
+    unittest.main()
index f4569fb005461fc5d6a0181b3fb5921dc321397e..c34ee578b5c83f15899edb7974fac14112270089 100644 (file)
@@ -8,7 +8,6 @@ import importlib.util
 import os
 import pickle
 import random
-import re
 import subprocess
 import sys
 import textwrap
@@ -91,51 +90,6 @@ class CAPITest(unittest.TestCase):
     def test_memoryview_from_NULL_pointer(self):
         self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer)
 
-    def test_exception(self):
-        raised_exception = ValueError("5")
-        new_exc = TypeError("TEST")
-        try:
-            raise raised_exception
-        except ValueError as e:
-            orig_sys_exception = sys.exception()
-            orig_exception = _testcapi.set_exception(new_exc)
-            new_sys_exception = sys.exception()
-            new_exception = _testcapi.set_exception(orig_exception)
-            reset_sys_exception = sys.exception()
-
-            self.assertEqual(orig_exception, e)
-
-            self.assertEqual(orig_exception, raised_exception)
-            self.assertEqual(orig_sys_exception, orig_exception)
-            self.assertEqual(reset_sys_exception, orig_exception)
-            self.assertEqual(new_exception, new_exc)
-            self.assertEqual(new_sys_exception, new_exception)
-        else:
-            self.fail("Exception not raised")
-
-    def test_exc_info(self):
-        raised_exception = ValueError("5")
-        new_exc = TypeError("TEST")
-        try:
-            raise raised_exception
-        except ValueError as e:
-            tb = e.__traceback__
-            orig_sys_exc_info = sys.exc_info()
-            orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
-            new_sys_exc_info = sys.exc_info()
-            new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
-            reset_sys_exc_info = sys.exc_info()
-
-            self.assertEqual(orig_exc_info[1], e)
-
-            self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
-            self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
-            self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
-            self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
-            self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
-        else:
-            self.assertTrue(False)
-
     @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.')
     def test_seq_bytes_to_charp_array(self):
         # Issue #15732: crash in _PySequence_BytesToCharpArray()
@@ -837,46 +791,6 @@ class CAPITest(unittest.TestCase):
         self.assertRaises(TypeError, pynumber_tobase, '123', 10)
         self.assertRaises(SystemError, pynumber_tobase, 123, 0)
 
-    def check_fatal_error(self, code, expected, not_expected=()):
-        with support.SuppressCrashReport():
-            rc, out, err = assert_python_failure('-sSI', '-c', code)
-
-        err = decode_stderr(err)
-        self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n',
-                      err)
-
-        match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
-                          err, re.MULTILINE)
-        if not match:
-            self.fail(f"Cannot find 'Extension modules:' in {err!r}")
-        modules = set(match.group(1).strip().split(', '))
-        total = int(match.group(2))
-
-        for name in expected:
-            self.assertIn(name, modules)
-        for name in not_expected:
-            self.assertNotIn(name, modules)
-        self.assertEqual(len(modules), total)
-
-    @support.requires_subprocess()
-    def test_fatal_error(self):
-        # By default, stdlib extension modules are ignored,
-        # but not test modules.
-        expected = ('_testcapi',)
-        not_expected = ('sys',)
-        code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
-        self.check_fatal_error(code, expected, not_expected)
-
-        # Mark _testcapi as stdlib module, but not sys
-        expected = ('sys',)
-        not_expected = ('_testcapi',)
-        code = textwrap.dedent('''
-            import _testcapi, sys
-            sys.stdlib_module_names = frozenset({"_testcapi"})
-            _testcapi.fatal_error(b"MESSAGE")
-        ''')
-        self.check_fatal_error(code, expected)
-
     def test_pyobject_repr_from_null(self):
         s = _testcapi.pyobject_repr_from_null()
         self.assertEqual(s, '<NULL>')
@@ -1214,9 +1128,9 @@ class TestPendingCalls(unittest.TestCase):
         self.pendingcalls_wait(l, n)
 
     def test_gen_get_code(self):
-       def genf(): yield
-       gen = genf()
-       self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
+        def genf(): yield
+        gen = genf()
+        self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
 
 
 class SubinterpreterTest(unittest.TestCase):
@@ -1641,44 +1555,5 @@ class Test_Pep523API(unittest.TestCase):
         self.do_test(func2)
 
 
-class Test_ErrSetAndRestore(unittest.TestCase):
-
-    def test_err_set_raised(self):
-        with self.assertRaises(ValueError):
-            _testcapi.err_set_raised(ValueError())
-        v = ValueError()
-        try:
-            _testcapi.err_set_raised(v)
-        except ValueError as ex:
-            self.assertIs(v, ex)
-
-    def test_err_restore(self):
-        with self.assertRaises(ValueError):
-            _testcapi.err_restore(ValueError)
-        with self.assertRaises(ValueError):
-            _testcapi.err_restore(ValueError, 1)
-        with self.assertRaises(ValueError):
-            _testcapi.err_restore(ValueError, 1, None)
-        with self.assertRaises(ValueError):
-            _testcapi.err_restore(ValueError, ValueError())
-        try:
-            _testcapi.err_restore(KeyError, "hi")
-        except KeyError as k:
-            self.assertEqual("hi", k.args[0])
-        try:
-            1/0
-        except Exception as e:
-            tb = e.__traceback__
-        with self.assertRaises(ValueError):
-            _testcapi.err_restore(ValueError, 1, tb)
-        with self.assertRaises(TypeError):
-            _testcapi.err_restore(ValueError, 1, 0)
-        try:
-            _testcapi.err_restore(ValueError, 1, tb)
-        except ValueError as v:
-            self.assertEqual(1, v.args[0])
-            self.assertIs(tb, v.__traceback__.tb_next)
-
-
 if __name__ == "__main__":
     unittest.main()
index d33cd82239995fb4a090f3af95bc973f4c4b11bd..7551e5b349430e19ce3916914df6852da7870bc7 100644 (file)
 @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
 @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
-@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c
+@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 
 # Some testing modules MUST be built as shared libraries.
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
new file mode 100644 (file)
index 0000000..43b88cc
--- /dev/null
@@ -0,0 +1,277 @@
+#include "parts.h"
+
+static PyObject *
+err_set_raised(PyObject *self, PyObject *exc)
+{
+    Py_INCREF(exc);
+    PyErr_SetRaisedException(exc);
+    assert(PyErr_Occurred());
+    return NULL;
+}
+
+static PyObject *
+err_restore(PyObject *self, PyObject *args) {
+    PyObject *type = NULL, *value = NULL, *traceback = NULL;
+    switch(PyTuple_Size(args)) {
+        case 3:
+            traceback = PyTuple_GetItem(args, 2);
+            Py_INCREF(traceback);
+            /* fall through */
+        case 2:
+            value = PyTuple_GetItem(args, 1);
+            Py_INCREF(value);
+            /* fall through */
+        case 1:
+            type = PyTuple_GetItem(args, 0);
+            Py_INCREF(type);
+            break;
+        default:
+            PyErr_SetString(PyExc_TypeError,
+                        "wrong number of arguments");
+            return NULL;
+    }
+    PyErr_Restore(type, value, traceback);
+    assert(PyErr_Occurred());
+    return NULL;
+}
+
+/* To test the format of exceptions as printed out. */
+static PyObject *
+exception_print(PyObject *self, PyObject *args)
+{
+    PyObject *value;
+    PyObject *tb = NULL;
+
+    if (!PyArg_ParseTuple(args, "O:exception_print", &value)) {
+        return NULL;
+    }
+
+    if (PyExceptionInstance_Check(value)) {
+        tb = PyException_GetTraceback(value);
+    }
+
+    PyErr_Display((PyObject *) Py_TYPE(value), value, tb);
+    Py_XDECREF(tb);
+
+    Py_RETURN_NONE;
+}
+
+/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException).
+   Run via Lib/test/test_exceptions.py */
+static PyObject *
+make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    const char *name;
+    const char *doc = NULL;
+    PyObject *base = NULL;
+    PyObject *dict = NULL;
+
+    static char *kwlist[] = {"name", "doc", "base", "dict", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                     "s|sOO:make_exception_with_doc", kwlist,
+                                     &name, &doc, &base, &dict))
+    {
+        return NULL;
+    }
+
+    return PyErr_NewExceptionWithDoc(name, doc, base, dict);
+}
+
+static PyObject *
+raise_exception(PyObject *self, PyObject *args)
+{
+    PyObject *exc;
+    int num_args;
+
+    if (!PyArg_ParseTuple(args, "Oi:raise_exception", &exc, &num_args)) {
+        return NULL;
+    }
+
+    PyObject *exc_args = PyTuple_New(num_args);
+    if (exc_args == NULL) {
+        return NULL;
+    }
+    for (int i = 0; i < num_args; ++i) {
+        PyObject *v = PyLong_FromLong(i);
+        if (v == NULL) {
+            Py_DECREF(exc_args);
+            return NULL;
+        }
+        PyTuple_SET_ITEM(exc_args, i, v);
+    }
+    PyErr_SetObject(exc, exc_args);
+    Py_DECREF(exc_args);
+    return NULL;
+}
+
+/* reliably raise a MemoryError */
+static PyObject *
+raise_memoryerror(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return PyErr_NoMemory();
+}
+
+static PyObject *
+test_fatal_error(PyObject *self, PyObject *args)
+{
+    char *message;
+    int release_gil = 0;
+    if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil)) {
+        return NULL;
+    }
+    if (release_gil) {
+        Py_BEGIN_ALLOW_THREADS
+        Py_FatalError(message);
+        Py_END_ALLOW_THREADS
+    }
+    else {
+        Py_FatalError(message);
+    }
+    // Py_FatalError() does not return, but exits the process.
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+test_set_exc_info(PyObject *self, PyObject *args)
+{
+    PyObject *new_type, *new_value, *new_tb;
+    PyObject *type, *value, *tb;
+    if (!PyArg_ParseTuple(args, "OOO:test_set_exc_info",
+                          &new_type, &new_value, &new_tb))
+    {
+        return NULL;
+    }
+
+    PyErr_GetExcInfo(&type, &value, &tb);
+
+    Py_INCREF(new_type);
+    Py_INCREF(new_value);
+    Py_INCREF(new_tb);
+    PyErr_SetExcInfo(new_type, new_value, new_tb);
+
+    PyObject *orig_exc = PyTuple_Pack(3,
+            type  ? type  : Py_None,
+            value ? value : Py_None,
+            tb    ? tb    : Py_None);
+    Py_XDECREF(type);
+    Py_XDECREF(value);
+    Py_XDECREF(tb);
+    return orig_exc;
+}
+
+static PyObject *
+test_set_exception(PyObject *self, PyObject *new_exc)
+{
+    PyObject *exc = PyErr_GetHandledException();
+    assert(PyExceptionInstance_Check(exc) || exc == NULL);
+
+    PyErr_SetHandledException(new_exc);
+    return exc;
+}
+
+static PyObject *
+test_write_unraisable_exc(PyObject *self, PyObject *args)
+{
+    PyObject *exc, *err_msg, *obj;
+    if (!PyArg_ParseTuple(args, "OOO", &exc, &err_msg, &obj)) {
+        return NULL;
+    }
+
+    const char *err_msg_utf8;
+    if (err_msg != Py_None) {
+        err_msg_utf8 = PyUnicode_AsUTF8(err_msg);
+        if (err_msg_utf8 == NULL) {
+            return NULL;
+        }
+    }
+    else {
+        err_msg_utf8 = NULL;
+    }
+
+    PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
+    _PyErr_WriteUnraisableMsg(err_msg_utf8, obj);
+    Py_RETURN_NONE;
+}
+
+/* To test the format of tracebacks as printed out. */
+static PyObject *
+traceback_print(PyObject *self, PyObject *args)
+{
+    PyObject *file;
+    PyObject *traceback;
+
+    if (!PyArg_ParseTuple(args, "OO:traceback_print",
+                            &traceback, &file))
+    {
+        return NULL;
+    }
+
+    if (PyTraceBack_Print(traceback, file) < 0) {
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+
+/*
+ * Define the PyRecurdingInfinitelyError_Type
+ */
+
+static PyTypeObject PyRecursingInfinitelyError_Type;
+
+static int
+recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *type = (PyObject *)&PyRecursingInfinitelyError_Type;
+
+    /* Instantiating this exception starts infinite recursion. */
+    Py_INCREF(type);
+    PyErr_SetObject(type, NULL);
+    return -1;
+}
+
+static PyTypeObject PyRecursingInfinitelyError_Type = {
+    .tp_name = "RecursingInfinitelyError",
+    .tp_basicsize = sizeof(PyBaseExceptionObject),
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+    .tp_doc = PyDoc_STR("Instantiating this exception starts infinite recursion."),
+    .tp_init = (initproc)recurse_infinitely_error_init,
+};
+
+static PyMethodDef test_methods[] = {
+    {"err_restore",             err_restore,                     METH_VARARGS},
+    {"err_set_raised",          err_set_raised,                  METH_O},
+    {"exception_print",         exception_print,                 METH_VARARGS},
+    {"fatal_error",             test_fatal_error,                METH_VARARGS,
+     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},
+    {"raise_exception",         raise_exception,                 METH_VARARGS},
+    {"raise_memoryerror",       raise_memoryerror,               METH_NOARGS},
+    {"set_exc_info",            test_set_exc_info,               METH_VARARGS},
+    {"set_exception",           test_set_exception,              METH_O},
+    {"traceback_print",         traceback_print,                 METH_VARARGS},
+    {"write_unraisable_exc",    test_write_unraisable_exc,       METH_VARARGS},
+    {NULL},
+};
+
+int
+_PyTestCapi_Init_Exceptions(PyObject *mod)
+{
+    PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception;
+    if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) {
+        return -1;
+    }
+    if (PyModule_AddObjectRef(mod, "RecursingInfinitelyError",
+                              (PyObject *)&PyRecursingInfinitelyError_Type) < 0)
+    {
+        return -1;
+    }
+
+    if (PyModule_AddFunctions(mod, test_methods) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
index 7ba3c4ebff8cde3c04fc24a20b39d20ce0a2ae1e..1689f186b833f60a3540d2ec5cfc449991f02043 100644 (file)
@@ -36,6 +36,7 @@ int _PyTestCapi_Init_Watchers(PyObject *module);
 int _PyTestCapi_Init_Long(PyObject *module);
 int _PyTestCapi_Init_Float(PyObject *module);
 int _PyTestCapi_Init_Structmember(PyObject *module);
+int _PyTestCapi_Init_Exceptions(PyObject *module);
 
 #ifdef LIMITED_API_AVAILABLE
 int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
index e2237d25a9a9400b84c38de831330bcc9d970d05..6bb424282a875d168bc3a400c5933adb48c2f7f0 100644 (file)
@@ -720,33 +720,6 @@ pyobject_bytes_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
     return PyObject_Bytes(NULL);
 }
 
-static PyObject *
-raise_exception(PyObject *self, PyObject *args)
-{
-    PyObject *exc;
-    PyObject *exc_args, *v;
-    int num_args, i;
-
-    if (!PyArg_ParseTuple(args, "Oi:raise_exception",
-                          &exc, &num_args))
-        return NULL;
-
-    exc_args = PyTuple_New(num_args);
-    if (exc_args == NULL)
-        return NULL;
-    for (i = 0; i < num_args; ++i) {
-        v = PyLong_FromLong(i);
-        if (v == NULL) {
-            Py_DECREF(exc_args);
-            return NULL;
-        }
-        PyTuple_SET_ITEM(exc_args, i, v);
-    }
-    PyErr_SetObject(exc, exc_args);
-    Py_DECREF(exc_args);
-    return NULL;
-}
-
 static PyObject *
 set_errno(PyObject *self, PyObject *args)
 {
@@ -759,40 +732,6 @@ set_errno(PyObject *self, PyObject *args)
     Py_RETURN_NONE;
 }
 
-static PyObject *
-test_set_exception(PyObject *self, PyObject *new_exc)
-{
-    PyObject *exc = PyErr_GetHandledException();
-    assert(PyExceptionInstance_Check(exc) || exc == NULL);
-
-    PyErr_SetHandledException(new_exc);
-    return exc;
-}
-
-static PyObject *
-test_set_exc_info(PyObject *self, PyObject *args)
-{
-    PyObject *orig_exc;
-    PyObject *new_type, *new_value, *new_tb;
-    PyObject *type, *value, *tb;
-    if (!PyArg_ParseTuple(args, "OOO:test_set_exc_info",
-                          &new_type, &new_value, &new_tb))
-        return NULL;
-
-    PyErr_GetExcInfo(&type, &value, &tb);
-
-    Py_INCREF(new_type);
-    Py_INCREF(new_value);
-    Py_INCREF(new_tb);
-    PyErr_SetExcInfo(new_type, new_value, new_tb);
-
-    orig_exc = PyTuple_Pack(3, type ? type : Py_None, value ? value : Py_None, tb ? tb : Py_None);
-    Py_XDECREF(type);
-    Py_XDECREF(value);
-    Py_XDECREF(tb);
-    return orig_exc;
-}
-
 /* test_thread_state spawns a thread of its own, and that thread releases
  * `thread_done` when it's finished.  The driver code has to know when the
  * thread finishes, because the thread uses a PyObject (the callable) that
@@ -1272,57 +1211,6 @@ profile_int(PyObject *self, PyObject* args)
 }
 #endif
 
-/* To test the format of tracebacks as printed out. */
-static PyObject *
-traceback_print(PyObject *self, PyObject *args)
-{
-    PyObject *file;
-    PyObject *traceback;
-    int result;
-
-    if (!PyArg_ParseTuple(args, "OO:traceback_print",
-                            &traceback, &file))
-        return NULL;
-
-    result = PyTraceBack_Print(traceback, file);
-    if (result < 0)
-        return NULL;
-    Py_RETURN_NONE;
-}
-
-/* To test the format of exceptions as printed out. */
-static PyObject *
-exception_print(PyObject *self, PyObject *args)
-{
-    PyObject *value;
-    PyObject *tb = NULL;
-
-    if (!PyArg_ParseTuple(args, "O:exception_print",
-                            &value)) {
-        return NULL;
-    }
-
-    if (PyExceptionInstance_Check(value)) {
-        tb = PyException_GetTraceback(value);
-    }
-
-    PyErr_Display((PyObject *) Py_TYPE(value), value, tb);
-    Py_XDECREF(tb);
-
-    Py_RETURN_NONE;
-}
-
-
-
-
-/* reliably raise a MemoryError */
-static PyObject *
-raise_memoryerror(PyObject *self, PyObject *Py_UNUSED(ignored))
-{
-    PyErr_NoMemory();
-    return NULL;
-}
-
 /* Issue 6012 */
 static PyObject *str1, *str2;
 static int
@@ -1368,26 +1256,6 @@ code_newempty(PyObject *self, PyObject *args)
     return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno);
 }
 
-/* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException).
-   Run via Lib/test/test_exceptions.py */
-static PyObject *
-make_exception_with_doc(PyObject *self, PyObject *args, PyObject *kwargs)
-{
-    const char *name;
-    const char *doc = NULL;
-    PyObject *base = NULL;
-    PyObject *dict = NULL;
-
-    static char *kwlist[] = {"name", "doc", "base", "dict", NULL};
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
-                    "s|sOO:make_exception_with_doc", kwlist,
-                                     &name, &doc, &base, &dict))
-        return NULL;
-
-    return PyErr_NewExceptionWithDoc(name, doc, base, dict);
-}
-
 static PyObject *
 make_memoryview_from_NULL_pointer(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
@@ -2491,31 +2359,6 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
 #endif
 
 
-static PyObject*
-test_write_unraisable_exc(PyObject *self, PyObject *args)
-{
-    PyObject *exc, *err_msg, *obj;
-    if (!PyArg_ParseTuple(args, "OOO", &exc, &err_msg, &obj)) {
-        return NULL;
-    }
-
-    const char *err_msg_utf8;
-    if (err_msg != Py_None) {
-        err_msg_utf8 = PyUnicode_AsUTF8(err_msg);
-        if (err_msg_utf8 == NULL) {
-            return NULL;
-        }
-    }
-    else {
-        err_msg_utf8 = NULL;
-    }
-
-    PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
-    _PyErr_WriteUnraisableMsg(err_msg_utf8, obj);
-    Py_RETURN_NONE;
-}
-
-
 static PyObject *
 sequence_getitem(PyObject *self, PyObject *args)
 {
@@ -2874,25 +2717,6 @@ test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored))
 }
 
 
-static PyObject *
-test_fatal_error(PyObject *self, PyObject *args)
-{
-    char *message;
-    int release_gil = 0;
-    if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil))
-        return NULL;
-    if (release_gil) {
-        Py_BEGIN_ALLOW_THREADS
-        Py_FatalError(message);
-        Py_END_ALLOW_THREADS
-    }
-    else {
-        Py_FatalError(message);
-    }
-    // Py_FatalError() does not return, but exits the process.
-    Py_RETURN_NONE;
-}
-
 // type->tp_version_tag
 static PyObject *
 type_get_version(PyObject *self, PyObject *type)
@@ -3492,46 +3316,9 @@ function_set_kw_defaults(PyObject *self, PyObject *args)
     Py_RETURN_NONE;
 }
 
-static PyObject *
-err_set_raised(PyObject *self, PyObject *exc)
-{
-    Py_INCREF(exc);
-    PyErr_SetRaisedException(exc);
-    assert(PyErr_Occurred());
-    return NULL;
-}
-
-static PyObject *
-err_restore(PyObject *self, PyObject *args) {
-    PyObject *type = NULL, *value = NULL, *traceback = NULL;
-    switch(PyTuple_Size(args)) {
-        case 3:
-            traceback = PyTuple_GetItem(args, 2);
-            Py_INCREF(traceback);
-            /* fall through */
-        case 2:
-            value = PyTuple_GetItem(args, 1);
-            Py_INCREF(value);
-            /* fall through */
-        case 1:
-            type = PyTuple_GetItem(args, 0);
-            Py_INCREF(type);
-            break;
-        default:
-            PyErr_SetString(PyExc_TypeError,
-                        "wrong number of arguments");
-            return NULL;
-    }
-    PyErr_Restore(type, value, traceback);
-    assert(PyErr_Occurred());
-    return NULL;
-}
-
 static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
 
 static PyMethodDef TestMethods[] = {
-    {"raise_exception",         raise_exception,                 METH_VARARGS},
-    {"raise_memoryerror",       raise_memoryerror,               METH_NOARGS},
     {"set_errno",               set_errno,                       METH_VARARGS},
     {"test_config",             test_config,                     METH_NOARGS},
     {"test_sizeof_c_types",     test_sizeof_c_types,             METH_NOARGS},
@@ -3574,15 +3361,9 @@ static PyMethodDef TestMethods[] = {
 #ifdef HAVE_GETTIMEOFDAY
     {"profile_int",             profile_int,                     METH_NOARGS},
 #endif
-    {"traceback_print",         traceback_print,                 METH_VARARGS},
-    {"exception_print",         exception_print,                 METH_VARARGS},
-    {"set_exception",           test_set_exception,              METH_O},
-    {"set_exc_info",            test_set_exc_info,               METH_VARARGS},
     {"argparsing",              argparsing,                      METH_VARARGS},
     {"code_newempty",           code_newempty,                   METH_VARARGS},
     {"eval_code_ex",            eval_eval_code_ex,               METH_VARARGS},
-    {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc),
-     METH_VARARGS | METH_KEYWORDS},
     {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,
      METH_NOARGS},
     {"crash_no_current_thread", crash_no_current_thread,         METH_NOARGS},
@@ -3633,7 +3414,6 @@ static PyMethodDef TestMethods[] = {
 #ifdef Py_REF_DEBUG
     {"negative_refcount", negative_refcount, METH_NOARGS},
 #endif
-    {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS},
     {"sequence_getitem", sequence_getitem, METH_VARARGS},
     {"sequence_setitem", sequence_setitem, METH_VARARGS},
     {"sequence_delitem", sequence_delitem, METH_VARARGS},
@@ -3653,8 +3433,6 @@ static PyMethodDef TestMethods[] = {
     {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS},
     {"test_py_is_macros", test_py_is_macros, METH_NOARGS},
     {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
-    {"fatal_error", test_fatal_error, METH_VARARGS,
-     PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")},
     {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
     {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
     {"frame_getlocals", frame_getlocals, METH_O, NULL},
@@ -3680,8 +3458,6 @@ static PyMethodDef TestMethods[] = {
     {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
     {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
     {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
-    {"err_set_raised", err_set_raised, METH_O, NULL},
-    {"err_restore", err_restore, METH_VARARGS, NULL},
     {NULL, NULL} /* sentinel */
 };
 
@@ -3902,61 +3678,6 @@ static PyTypeObject awaitType = {
 };
 
 
-static int recurse_infinitely_error_init(PyObject *, PyObject *, PyObject *);
-
-static PyTypeObject PyRecursingInfinitelyError_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "RecursingInfinitelyError",   /* tp_name */
-    sizeof(PyBaseExceptionObject), /* tp_basicsize */
-    0,                          /* tp_itemsize */
-    0,                          /* tp_dealloc */
-    0,                          /* tp_vectorcall_offset */
-    0,                          /* tp_getattr */
-    0,                          /* tp_setattr */
-    0,                          /* tp_as_async */
-    0,                          /* tp_repr */
-    0,                          /* tp_as_number */
-    0,                          /* tp_as_sequence */
-    0,                          /* tp_as_mapping */
-    0,                          /* tp_hash */
-    0,                          /* tp_call */
-    0,                          /* tp_str */
-    0,                          /* tp_getattro */
-    0,                          /* tp_setattro */
-    0,                          /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("Instantiating this exception starts infinite recursion."), /* tp_doc */
-    0,                          /* tp_traverse */
-    0,                          /* tp_clear */
-    0,                          /* tp_richcompare */
-    0,                          /* tp_weaklistoffset */
-    0,                          /* tp_iter */
-    0,                          /* tp_iternext */
-    0,                          /* tp_methods */
-    0,                          /* tp_members */
-    0,                          /* tp_getset */
-    0,                          /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    0,                          /* tp_dictoffset */
-    (initproc)recurse_infinitely_error_init, /* tp_init */
-    0,                          /* tp_alloc */
-    0,                          /* tp_new */
-};
-
-static int
-recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds)
-{
-    PyObject *type = (PyObject *)&PyRecursingInfinitelyError_Type;
-
-    /* Instantiating this exception starts infinite recursion. */
-    Py_INCREF(type);
-    PyErr_SetObject(type, NULL);
-    return -1;
-}
-
-
 /* Test bpo-35983: create a subclass of "list" which checks that instances
  * are not deallocated twice */
 
@@ -4283,14 +4004,6 @@ PyInit__testcapi(void)
     Py_INCREF(&MethStatic_Type);
     PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
 
-    PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception;
-    if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) {
-        return NULL;
-    }
-    Py_INCREF(&PyRecursingInfinitelyError_Type);
-    PyModule_AddObject(m, "RecursingInfinitelyError",
-                       (PyObject *)&PyRecursingInfinitelyError_Type);
-
     PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
     PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
     PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));
@@ -4368,6 +4081,9 @@ PyInit__testcapi(void)
     if (_PyTestCapi_Init_Structmember(m) < 0) {
         return NULL;
     }
+    if (_PyTestCapi_Init_Exceptions(m) < 0) {
+        return NULL;
+    }
 
 #ifndef LIMITED_API_AVAILABLE
     PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);
index 58bf4e1eacbf219507c382876bca554379cf562c..742eb3ed2d90569a51c84fad127778a7a11d523d 100644 (file)
     <ClCompile Include="..\Modules\_testcapi\float.c" />
     <ClCompile Include="..\Modules\_testcapi\long.c" />
     <ClCompile Include="..\Modules\_testcapi\structmember.c" />
+    <ClCompile Include="..\Modules\_testcapi\exceptions.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
index 101c5322761634881f1fd29d038687c2484b7d72..ab5afc150c32f5ce98281b7b025ab8ccfee987a8 100644 (file)
@@ -51,6 +51,9 @@
     <ClCompile Include="..\Modules\_testcapi\structmember.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\Modules\_testcapi\exceptions.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc">