summaryrefslogtreecommitdiffstats
path: root/Modules/_testcapi
diff options
context:
space:
mode:
authorErlend E. Aasland <erlend.aasland@protonmail.com>2023-02-23 15:03:13 (GMT)
committerGitHub <noreply@github.com>2023-02-23 15:03:13 (GMT)
commitefc985a714b6f43c43ae629183f95618054422ae (patch)
tree06d0f62d91e22372a6e75d9ad8061767b9689f95 /Modules/_testcapi
parente07b304bb004e1298283c82bd135dd5ef96a90cc (diff)
downloadcpython-efc985a714b6f43c43ae629183f95618054422ae.zip
cpython-efc985a714b6f43c43ae629183f95618054422ae.tar.gz
cpython-efc985a714b6f43c43ae629183f95618054422ae.tar.bz2
gh-93649: Split exception tests from _testcapimodule.c (GH-102173)
Automerge-Triggered-By: GH:erlend-aasland
Diffstat (limited to 'Modules/_testcapi')
-rw-r--r--Modules/_testcapi/exceptions.c277
-rw-r--r--Modules/_testcapi/parts.h1
2 files changed, 278 insertions, 0 deletions
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
new file mode 100644
index 0000000..43b88cc
--- /dev/null
+++ b/Modules/_testcapi/exceptions.c
@@ -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;
+}
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index 7ba3c4e..1689f18 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -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);