From e232025025c8bd07c1d1b12a583a80c4a673f077 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 18 Jan 2021 18:24:29 +0100 Subject: [PATCH] bpo-42923: Add Py_FatalError() test in test_capi (GH-24240) Move faulthandler._fatal_error() to _testcapi.fatal_error(). --- Lib/test/test_capi.py | 9 +++++++++ Lib/test/test_faulthandler.py | 30 ++++++++++++++---------------- Modules/_testcapimodule.c | 23 +++++++++++++++++++++++ Modules/faulthandler.c | 25 ++----------------------- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index a4ebe4a0a1b..0d5c97dcc2a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -547,6 +547,15 @@ class CAPITest(unittest.TestCase): self.assertRaises(TypeError, pynumber_tobase, '123', 10) self.assertRaises(SystemError, pynumber_tobase, 123, 0) + def test_fatal_error(self): + code = 'import _testcapi; _testcapi.fatal_error(b"MESSAGE")' + with support.SuppressCrashReport(): + rc, out, err = assert_python_failure('-sSI', '-c', code) + + err = err.replace(b'\r', b'').decode('ascii', 'replace') + self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n', + err) + class TestPendingCalls(unittest.TestCase): diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 80c1f7d90ad..bc61aab9c0f 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -227,25 +227,23 @@ class FaultHandlerTests(unittest.TestCase): 5, 'Illegal instruction') + def check_fatal_error_func(self, release_gil): + # Test that Py_FatalError() dumps a traceback + with support.SuppressCrashReport(): + self.check_fatal_error(f""" + import _testcapi + _testcapi.fatal_error(b'xyz', {release_gil}) + """, + 2, + 'xyz', + func='test_fatal_error', + py_fatal_error=True) + def test_fatal_error(self): - self.check_fatal_error(""" - import faulthandler - faulthandler._fatal_error(b'xyz') - """, - 2, - 'xyz', - func='faulthandler_fatal_error_py', - py_fatal_error=True) + self.check_fatal_error_func(False) def test_fatal_error_without_gil(self): - self.check_fatal_error(""" - import faulthandler - faulthandler._fatal_error(b'xyz', True) - """, - 2, - 'xyz', - func='faulthandler_fatal_error_py', - py_fatal_error=True) + self.check_fatal_error_func(True) @unittest.skipIf(sys.platform.startswith('openbsd'), "Issue #12868: sigaltstack() doesn't work on " diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4f97927fa23..2a5b3d9c50b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5665,6 +5665,27 @@ test_refcount(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; +} + + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, @@ -5938,6 +5959,8 @@ static PyMethodDef TestMethods[] = { {"without_gc", without_gc, METH_O}, {"test_set_type_size", test_set_type_size, METH_NOARGS}, {"test_refcount", test_refcount, METH_NOARGS}, + {"fatal_error", test_fatal_error, METH_VARARGS, + PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 67fe1ca9fff..fe5dbc1cc0f 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1,6 +1,6 @@ #include "Python.h" -#include "pycore_initconfig.h" -#include "pycore_traceback.h" +#include "pycore_initconfig.h" // _PyStatus_ERR +#include "pycore_traceback.h" // _Py_DumpTracebackThreads #include #include #include @@ -1123,25 +1123,6 @@ faulthandler_sigabrt(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -faulthandler_fatal_error_py(PyObject *self, PyObject *args) -{ - char *message; - int release_gil = 0; - if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil)) - return NULL; - faulthandler_suppress_crash_report(); - if (release_gil) { - Py_BEGIN_ALLOW_THREADS - Py_FatalError(message); - Py_END_ALLOW_THREADS - } - else { - Py_FatalError(message); - } - Py_RETURN_NONE; -} - #if defined(FAULTHANDLER_USE_ALT_STACK) #define FAULTHANDLER_STACK_OVERFLOW @@ -1278,8 +1259,6 @@ static PyMethodDef module_methods[] = { PyDoc_STR("_sigabrt(): raise a SIGABRT signal")}, {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS, PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, - {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, - PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, #ifdef FAULTHANDLER_STACK_OVERFLOW {"_stack_overflow", faulthandler_stack_overflow, METH_NOARGS, PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")},