Commit 56e8c29a authored by Victor Stinner's avatar Victor Stinner

Issue #22018: Add _testcapi.raise_signal()

- Use _testcapi.raise_signal() in test_signal
- close also os.pipe() file descriptors in some test_signal tests where they
  were not closed properly
- Remove faulthandler._sigill() and faulthandler._sigbus(): reuse
  _testcapi.raise_signal() in test_faulthandler
parent 569a7fa1
...@@ -16,6 +16,10 @@ try: ...@@ -16,6 +16,10 @@ try:
HAVE_THREADS = True HAVE_THREADS = True
except ImportError: except ImportError:
HAVE_THREADS = False HAVE_THREADS = False
try:
import _testcapi
except ImportError:
_testcapi = None
TIMEOUT = 0.5 TIMEOUT = 0.5
...@@ -133,26 +137,32 @@ faulthandler._sigfpe() ...@@ -133,26 +137,32 @@ faulthandler._sigfpe()
3, 3,
'Floating point exception') 'Floating point exception')
@unittest.skipIf(not hasattr(faulthandler, '_sigbus'), @unittest.skipIf(_testcapi is None, 'need _testcapi')
"need faulthandler._sigbus()") @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS')
def test_sigbus(self): def test_sigbus(self):
self.check_fatal_error(""" self.check_fatal_error("""
import _testcapi
import faulthandler import faulthandler
import signal
faulthandler.enable() faulthandler.enable()
faulthandler._sigbus() _testcapi.raise_signal(signal.SIGBUS)
""".strip(), """.strip(),
3, 6,
'Bus error') 'Bus error')
@unittest.skipIf(not hasattr(faulthandler, '_sigill'), @unittest.skipIf(_testcapi is None, 'need _testcapi')
"need faulthandler._sigill()") @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL')
def test_sigill(self): def test_sigill(self):
self.check_fatal_error(""" self.check_fatal_error("""
import _testcapi
import faulthandler import faulthandler
import signal
faulthandler.enable() faulthandler.enable()
faulthandler._sigill() _testcapi.raise_signal(signal.SIGILL)
""".strip(), """.strip(),
3, 6,
'Illegal instruction') 'Illegal instruction')
def test_fatal_error(self): def test_fatal_error(self):
......
...@@ -15,6 +15,10 @@ try: ...@@ -15,6 +15,10 @@ try:
import threading import threading
except ImportError: except ImportError:
threading = None threading = None
try:
import _testcapi
except ImportError:
_testcapi = None
class HandlerBCalled(Exception): class HandlerBCalled(Exception):
...@@ -250,12 +254,27 @@ class WakeupFDTests(unittest.TestCase): ...@@ -250,12 +254,27 @@ class WakeupFDTests(unittest.TestCase):
fd = support.make_bad_fd() fd = support.make_bad_fd()
self.assertRaises(ValueError, signal.set_wakeup_fd, fd) self.assertRaises(ValueError, signal.set_wakeup_fd, fd)
def test_set_wakeup_fd_result(self):
r1, w1 = os.pipe()
os.close(r1)
self.addCleanup(os.close, w1)
r2, w2 = os.pipe()
os.close(r2)
self.addCleanup(os.close, w2)
signal.set_wakeup_fd(w1)
self.assertIs(signal.set_wakeup_fd(w2), w1)
self.assertIs(signal.set_wakeup_fd(-1), w2)
self.assertIs(signal.set_wakeup_fd(-1), -1)
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
class WakeupSignalTests(unittest.TestCase): class WakeupSignalTests(unittest.TestCase):
@unittest.skipIf(_testcapi is None, 'need _testcapi')
def check_wakeup(self, test_body, *signals, ordered=True): def check_wakeup(self, test_body, *signals, ordered=True):
# use a subprocess to have only one thread # use a subprocess to have only one thread
code = """if 1: code = """if 1:
import _testcapi
import fcntl import fcntl
import os import os
import signal import signal
...@@ -294,17 +313,18 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -294,17 +313,18 @@ class WakeupSignalTests(unittest.TestCase):
assert_python_ok('-c', code) assert_python_ok('-c', code)
@unittest.skipIf(_testcapi is None, 'need _testcapi')
def test_wakeup_write_error(self): def test_wakeup_write_error(self):
# Issue #16105: write() errors in the C signal handler should not # Issue #16105: write() errors in the C signal handler should not
# pass silently. # pass silently.
# Use a subprocess to have only one thread. # Use a subprocess to have only one thread.
code = """if 1: code = """if 1:
import _testcapi
import errno import errno
import fcntl import fcntl
import os import os
import signal import signal
import sys import sys
import time
from test.support import captured_stderr from test.support import captured_stderr
def handler(signum, frame): def handler(signum, frame):
...@@ -319,8 +339,7 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -319,8 +339,7 @@ class WakeupSignalTests(unittest.TestCase):
signal.set_wakeup_fd(r) signal.set_wakeup_fd(r)
try: try:
with captured_stderr() as err: with captured_stderr() as err:
signal.alarm(1) _testcapi.raise_signal(signal.SIGALRM)
time.sleep(5.0)
except ZeroDivisionError: except ZeroDivisionError:
# An ignored exception should have been printed out on stderr # An ignored exception should have been printed out on stderr
err = err.getvalue() err = err.getvalue()
...@@ -331,6 +350,9 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -331,6 +350,9 @@ class WakeupSignalTests(unittest.TestCase):
raise AssertionError(err) raise AssertionError(err)
else: else:
raise AssertionError("ZeroDivisionError not raised") raise AssertionError("ZeroDivisionError not raised")
os.close(r)
os.close(w)
""" """
r, w = os.pipe() r, w = os.pipe()
try: try:
...@@ -394,9 +416,10 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -394,9 +416,10 @@ class WakeupSignalTests(unittest.TestCase):
def test_signum(self): def test_signum(self):
self.check_wakeup("""def test(): self.check_wakeup("""def test():
import _testcapi
signal.signal(signal.SIGUSR1, handler) signal.signal(signal.SIGUSR1, handler)
os.kill(os.getpid(), signal.SIGUSR1) _testcapi.raise_signal(signal.SIGUSR1)
os.kill(os.getpid(), signal.SIGALRM) _testcapi.raise_signal(signal.SIGALRM)
""", signal.SIGUSR1, signal.SIGALRM) """, signal.SIGUSR1, signal.SIGALRM)
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
...@@ -410,8 +433,8 @@ class WakeupSignalTests(unittest.TestCase): ...@@ -410,8 +433,8 @@ class WakeupSignalTests(unittest.TestCase):
signal.signal(signum2, handler) signal.signal(signum2, handler)
signal.pthread_sigmask(signal.SIG_BLOCK, (signum1, signum2)) signal.pthread_sigmask(signal.SIG_BLOCK, (signum1, signum2))
os.kill(os.getpid(), signum1) _testcapi.raise_signal(signum1)
os.kill(os.getpid(), signum2) _testcapi.raise_signal(signum2)
# Unblocking the 2 signals calls the C signal handler twice # Unblocking the 2 signals calls the C signal handler twice
signal.pthread_sigmask(signal.SIG_UNBLOCK, (signum1, signum2)) signal.pthread_sigmask(signal.SIG_UNBLOCK, (signum1, signum2))
""", signal.SIGUSR1, signal.SIGUSR2, ordered=False) """, signal.SIGUSR1, signal.SIGUSR2, ordered=False)
...@@ -447,18 +470,22 @@ class SiginterruptTest(unittest.TestCase): ...@@ -447,18 +470,22 @@ class SiginterruptTest(unittest.TestCase):
sys.stdout.flush() sys.stdout.flush()
# run the test twice # run the test twice
for loop in range(2): try:
# send a SIGALRM in a second (during the read) for loop in range(2):
signal.alarm(1) # send a SIGALRM in a second (during the read)
try: signal.alarm(1)
# blocking call: read from a pipe without data try:
os.read(r, 1) # blocking call: read from a pipe without data
except OSError as err: os.read(r, 1)
if err.errno != errno.EINTR: except OSError as err:
raise if err.errno != errno.EINTR:
else: raise
sys.exit(2) else:
sys.exit(3) sys.exit(2)
sys.exit(3)
finally:
os.close(r)
os.close(w)
""" % (interrupt,) """ % (interrupt,)
with spawn_python('-c', code) as process: with spawn_python('-c', code) as process:
try: try:
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <float.h> #include <float.h>
#include "structmember.h" #include "structmember.h"
#include "datetime.h" #include "datetime.h"
#include <signal.h>
#ifdef WITH_THREAD #ifdef WITH_THREAD
#include "pythread.h" #include "pythread.h"
...@@ -3063,6 +3064,24 @@ exit: ...@@ -3063,6 +3064,24 @@ exit:
} }
#endif /* WITH_THREAD */ #endif /* WITH_THREAD */
static PyObject*
test_raise_signal(PyObject* self, PyObject *args)
{
int signum, err;
if (PyArg_ParseTuple(args, "i:raise_signal", &signum) < 0)
return NULL;
err = raise(signum);
if (err)
return PyErr_SetFromErrno(PyExc_OSError);
if (PyErr_CheckSignals() < 0)
return NULL;
Py_RETURN_NONE;
}
static PyMethodDef TestMethods[] = { static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS},
...@@ -3198,6 +3217,8 @@ static PyMethodDef TestMethods[] = { ...@@ -3198,6 +3217,8 @@ static PyMethodDef TestMethods[] = {
{"docstring_with_signature_with_defaults", {"docstring_with_signature_with_defaults",
(PyCFunction)test_with_docstring, METH_NOARGS, (PyCFunction)test_with_docstring, METH_NOARGS,
docstring_with_signature_with_defaults}, docstring_with_signature_with_defaults},
{"raise_signal",
(PyCFunction)test_raise_signal, METH_VARARGS},
#ifdef WITH_THREAD #ifdef WITH_THREAD
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O,
PyDoc_STR("set_error_class(error_class) -> None")}, PyDoc_STR("set_error_class(error_class) -> None")},
......
...@@ -874,24 +874,6 @@ faulthandler_sigabrt(PyObject *self, PyObject *args) ...@@ -874,24 +874,6 @@ faulthandler_sigabrt(PyObject *self, PyObject *args)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#ifdef SIGBUS
static PyObject *
faulthandler_sigbus(PyObject *self, PyObject *args)
{
raise(SIGBUS);
Py_RETURN_NONE;
}
#endif
#ifdef SIGILL
static PyObject *
faulthandler_sigill(PyObject *self, PyObject *args)
{
raise(SIGILL);
Py_RETURN_NONE;
}
#endif
static PyObject * static PyObject *
faulthandler_fatal_error_py(PyObject *self, PyObject *args) faulthandler_fatal_error_py(PyObject *self, PyObject *args)
{ {
...@@ -1012,14 +994,6 @@ static PyMethodDef module_methods[] = { ...@@ -1012,14 +994,6 @@ static PyMethodDef module_methods[] = {
PyDoc_STR("_sigabrt(): raise a SIGABRT signal")}, PyDoc_STR("_sigabrt(): raise a SIGABRT signal")},
{"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS, {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, PyDoc_STR("_sigfpe(): raise a SIGFPE signal")},
#ifdef SIGBUS
{"_sigbus", (PyCFunction)faulthandler_sigbus, METH_NOARGS,
PyDoc_STR("_sigbus(): raise a SIGBUS signal")},
#endif
#ifdef SIGILL
{"_sigill", (PyCFunction)faulthandler_sigill, METH_NOARGS,
PyDoc_STR("_sigill(): raise a SIGILL signal")},
#endif
{"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS,
PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")},
#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment