Skip to content

Commit e232025

Browse files
authored
bpo-42923: Add Py_FatalError() test in test_capi (GH-24240)
Move faulthandler._fatal_error() to _testcapi.fatal_error().
1 parent 998ae1f commit e232025

File tree

4 files changed

+48
-39
lines changed

4 files changed

+48
-39
lines changed

Lib/test/test_capi.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,15 @@ def test_pynumber_tobase(self):
547547
self.assertRaises(TypeError, pynumber_tobase, '123', 10)
548548
self.assertRaises(SystemError, pynumber_tobase, 123, 0)
549549

550+
def test_fatal_error(self):
551+
code = 'import _testcapi; _testcapi.fatal_error(b"MESSAGE")'
552+
with support.SuppressCrashReport():
553+
rc, out, err = assert_python_failure('-sSI', '-c', code)
554+
555+
err = err.replace(b'\r', b'').decode('ascii', 'replace')
556+
self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n',
557+
err)
558+
550559

551560
class TestPendingCalls(unittest.TestCase):
552561

Lib/test/test_faulthandler.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -227,25 +227,23 @@ def test_sigill(self):
227227
5,
228228
'Illegal instruction')
229229

230+
def check_fatal_error_func(self, release_gil):
231+
# Test that Py_FatalError() dumps a traceback
232+
with support.SuppressCrashReport():
233+
self.check_fatal_error(f"""
234+
import _testcapi
235+
_testcapi.fatal_error(b'xyz', {release_gil})
236+
""",
237+
2,
238+
'xyz',
239+
func='test_fatal_error',
240+
py_fatal_error=True)
241+
230242
def test_fatal_error(self):
231-
self.check_fatal_error("""
232-
import faulthandler
233-
faulthandler._fatal_error(b'xyz')
234-
""",
235-
2,
236-
'xyz',
237-
func='faulthandler_fatal_error_py',
238-
py_fatal_error=True)
243+
self.check_fatal_error_func(False)
239244

240245
def test_fatal_error_without_gil(self):
241-
self.check_fatal_error("""
242-
import faulthandler
243-
faulthandler._fatal_error(b'xyz', True)
244-
""",
245-
2,
246-
'xyz',
247-
func='faulthandler_fatal_error_py',
248-
py_fatal_error=True)
246+
self.check_fatal_error_func(True)
249247

250248
@unittest.skipIf(sys.platform.startswith('openbsd'),
251249
"Issue #12868: sigaltstack() doesn't work on "

Modules/_testcapimodule.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5665,6 +5665,27 @@ test_refcount(PyObject *self, PyObject *Py_UNUSED(ignored))
56655665
}
56665666

56675667

5668+
static PyObject *
5669+
test_fatal_error(PyObject *self, PyObject *args)
5670+
{
5671+
char *message;
5672+
int release_gil = 0;
5673+
if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil))
5674+
return NULL;
5675+
if (release_gil) {
5676+
Py_BEGIN_ALLOW_THREADS
5677+
Py_FatalError(message);
5678+
Py_END_ALLOW_THREADS
5679+
}
5680+
else {
5681+
Py_FatalError(message);
5682+
}
5683+
// Py_FatalError() does not return, but exits the process.
5684+
Py_RETURN_NONE;
5685+
}
5686+
5687+
5688+
56685689
static PyMethodDef TestMethods[] = {
56695690
{"raise_exception", raise_exception, METH_VARARGS},
56705691
{"raise_memoryerror", raise_memoryerror, METH_NOARGS},
@@ -5938,6 +5959,8 @@ static PyMethodDef TestMethods[] = {
59385959
{"without_gc", without_gc, METH_O},
59395960
{"test_set_type_size", test_set_type_size, METH_NOARGS},
59405961
{"test_refcount", test_refcount, METH_NOARGS},
5962+
{"fatal_error", test_fatal_error, METH_VARARGS,
5963+
PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")},
59415964
{NULL, NULL} /* sentinel */
59425965
};
59435966

Modules/faulthandler.c

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "Python.h"
2-
#include "pycore_initconfig.h"
3-
#include "pycore_traceback.h"
2+
#include "pycore_initconfig.h" // _PyStatus_ERR
3+
#include "pycore_traceback.h" // _Py_DumpTracebackThreads
44
#include <signal.h>
55
#include <object.h>
66
#include <frameobject.h>
@@ -1123,25 +1123,6 @@ faulthandler_sigabrt(PyObject *self, PyObject *args)
11231123
Py_RETURN_NONE;
11241124
}
11251125

1126-
static PyObject *
1127-
faulthandler_fatal_error_py(PyObject *self, PyObject *args)
1128-
{
1129-
char *message;
1130-
int release_gil = 0;
1131-
if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil))
1132-
return NULL;
1133-
faulthandler_suppress_crash_report();
1134-
if (release_gil) {
1135-
Py_BEGIN_ALLOW_THREADS
1136-
Py_FatalError(message);
1137-
Py_END_ALLOW_THREADS
1138-
}
1139-
else {
1140-
Py_FatalError(message);
1141-
}
1142-
Py_RETURN_NONE;
1143-
}
1144-
11451126
#if defined(FAULTHANDLER_USE_ALT_STACK)
11461127
#define FAULTHANDLER_STACK_OVERFLOW
11471128

@@ -1278,8 +1259,6 @@ static PyMethodDef module_methods[] = {
12781259
PyDoc_STR("_sigabrt(): raise a SIGABRT signal")},
12791260
{"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
12801261
PyDoc_STR("_sigfpe(): raise a SIGFPE signal")},
1281-
{"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS,
1282-
PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")},
12831262
#ifdef FAULTHANDLER_STACK_OVERFLOW
12841263
{"_stack_overflow", faulthandler_stack_overflow, METH_NOARGS,
12851264
PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")},

0 commit comments

Comments
 (0)