bpo-35568: add 'raise_signal' function (GH-11335)

As in title, expose C `raise` function as `raise_function` in `signal` module. Also drop existing `raise_signal` in `_testcapi` module and replace all usages with new function.


https://bugs.python.org/issue35568
This commit is contained in:
Vladimir Matveev 2019-01-08 01:58:25 -08:00 committed by Miss Islington (bot)
parent e61cc481e0
commit c24c6c2c93
10 changed files with 122 additions and 47 deletions

View File

@ -237,6 +237,13 @@ The :mod:`signal` module defines the following functions:
:func:`sigpending`.
.. function:: raise_signal(signum)
Sends a signal to the calling process. Returns nothing.
.. versionadded:: 3.8
.. function:: pthread_kill(thread_id, signalnum)
Send the signal *signalnum* to the thread *thread_id*, another thread in the

View File

@ -45,7 +45,7 @@ class ProactorLoopCtrlC(test_utils.TestCase):
def SIGINT_after_delay():
time.sleep(1)
_testcapi.raise_signal(signal.SIGINT)
signal.raise_signal(signal.SIGINT)
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
l = asyncio.get_event_loop()

View File

@ -198,14 +198,13 @@ class FaultHandlerTests(unittest.TestCase):
@skip_segfault_on_android
def test_sigbus(self):
self.check_fatal_error("""
import _testcapi
import faulthandler
import signal
faulthandler.enable()
_testcapi.raise_signal(signal.SIGBUS)
signal.raise_signal(signal.SIGBUS)
""",
6,
5,
'Bus error')
@unittest.skipIf(_testcapi is None, 'need _testcapi')
@ -213,14 +212,13 @@ class FaultHandlerTests(unittest.TestCase):
@skip_segfault_on_android
def test_sigill(self):
self.check_fatal_error("""
import _testcapi
import faulthandler
import signal
faulthandler.enable()
_testcapi.raise_signal(signal.SIGILL)
signal.raise_signal(signal.SIGILL)
""",
6,
5,
'Illegal instruction')
def test_fatal_error(self):

View File

@ -1596,8 +1596,8 @@ class TestPosixSpawn(unittest.TestCase):
'need signal.pthread_sigmask()')
def test_setsigmask(self):
code = textwrap.dedent("""\
import _testcapi, signal
_testcapi.raise_signal(signal.SIGUSR1)""")
import signal
signal.raise_signal(signal.SIGUSR1)""")
pid = posix.posix_spawn(
sys.executable,
@ -1627,8 +1627,8 @@ class TestPosixSpawn(unittest.TestCase):
def test_setsigdef(self):
original_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN)
code = textwrap.dedent("""\
import _testcapi, signal
_testcapi.raise_signal(signal.SIGUSR1)""")
import signal
signal.raise_signal(signal.SIGUSR1)""")
try:
pid = posix.posix_spawn(
sys.executable,

View File

@ -26,9 +26,8 @@ ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..')
ROOT_DIR = os.path.abspath(os.path.normpath(ROOT_DIR))
TEST_INTERRUPTED = textwrap.dedent("""
from signal import SIGINT
from signal import SIGINT, raise_signal
try:
from _testcapi import raise_signal
raise_signal(SIGINT)
except ImportError:
import os

View File

@ -1,3 +1,4 @@
import errno
import os
import random
import signal
@ -254,7 +255,7 @@ class WakeupSignalTests(unittest.TestCase):
signal.set_wakeup_fd(r)
try:
with captured_stderr() as err:
_testcapi.raise_signal(signal.SIGALRM)
signal.raise_signal(signal.SIGALRM)
except ZeroDivisionError:
# An ignored exception should have been printed out on stderr
err = err.getvalue()
@ -348,10 +349,9 @@ class WakeupSignalTests(unittest.TestCase):
def test_signum(self):
self.check_wakeup("""def test():
import _testcapi
signal.signal(signal.SIGUSR1, handler)
_testcapi.raise_signal(signal.SIGUSR1)
_testcapi.raise_signal(signal.SIGALRM)
signal.raise_signal(signal.SIGUSR1)
signal.raise_signal(signal.SIGALRM)
""", signal.SIGUSR1, signal.SIGALRM)
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
@ -365,8 +365,8 @@ class WakeupSignalTests(unittest.TestCase):
signal.signal(signum2, handler)
signal.pthread_sigmask(signal.SIG_BLOCK, (signum1, signum2))
_testcapi.raise_signal(signum1)
_testcapi.raise_signal(signum2)
signal.raise_signal(signum1)
signal.raise_signal(signum2)
# Unblocking the 2 signals calls the C signal handler twice
signal.pthread_sigmask(signal.SIG_UNBLOCK, (signum1, signum2))
""", signal.SIGUSR1, signal.SIGUSR2, ordered=False)
@ -396,7 +396,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
write.setblocking(False)
signal.set_wakeup_fd(write.fileno())
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
data = read.recv(1)
if not data:
@ -445,7 +445,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
write.close()
with captured_stderr() as err:
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
err = err.getvalue()
if ('Exception ignored when trying to {action} to the signal wakeup fd'
@ -519,7 +519,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
signal.set_wakeup_fd(write.fileno())
with captured_stderr() as err:
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
err = err.getvalue()
if msg not in err:
@ -530,7 +530,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
signal.set_wakeup_fd(write.fileno(), warn_on_full_buffer=True)
with captured_stderr() as err:
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
err = err.getvalue()
if msg not in err:
@ -541,7 +541,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
signal.set_wakeup_fd(write.fileno(), warn_on_full_buffer=False)
with captured_stderr() as err:
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
err = err.getvalue()
if err != "":
@ -553,7 +553,7 @@ class WakeupSocketSignalTests(unittest.TestCase):
signal.set_wakeup_fd(write.fileno())
with captured_stderr() as err:
_testcapi.raise_signal(signum)
signal.raise_signal(signum)
err = err.getvalue()
if msg not in err:
@ -1214,6 +1214,38 @@ class StressTest(unittest.TestCase):
# Python handler
self.assertEqual(len(sigs), N, "Some signals were lost")
class RaiseSignalTest(unittest.TestCase):
def test_sigint(self):
try:
signal.raise_signal(signal.SIGINT)
self.fail("Expected KeyInterrupt")
except KeyboardInterrupt:
pass
@unittest.skipIf(sys.platform != "win32", "Windows specific test")
def test_invalid_argument(self):
try:
SIGHUP = 1 # not supported on win32
signal.raise_signal(SIGHUP)
self.fail("OSError (Invalid argument) expected")
except OSError as e:
if e.errno == errno.EINVAL:
pass
else:
raise
def test_handler(self):
is_ok = False
def handler(a, b):
nonlocal is_ok
is_ok = True
old_signal = signal.signal(signal.SIGINT, handler)
self.addCleanup(signal.signal, signal.SIGINT, old_signal)
signal.raise_signal(signal.SIGINT)
self.assertTrue(is_ok)
def tearDownModule():
support.reap_children()

View File

@ -0,0 +1 @@
Expose ``raise(signum)`` as `raise_signal`

View File

@ -3859,25 +3859,6 @@ exit:
return res;
}
static PyObject*
test_raise_signal(PyObject* self, PyObject *args)
{
int signum, err;
if (!PyArg_ParseTuple(args, "i:raise_signal", &signum)) {
return NULL;
}
err = raise(signum);
if (err)
return PyErr_SetFromErrno(PyExc_OSError);
if (PyErr_CheckSignals() < 0)
return NULL;
Py_RETURN_NONE;
}
/* marshal */
static PyObject*
@ -4908,8 +4889,6 @@ static PyMethodDef TestMethods[] = {
{"docstring_with_signature_with_defaults",
(PyCFunction)test_with_docstring, METH_NOARGS,
docstring_with_signature_with_defaults},
{"raise_signal",
(PyCFunction)test_raise_signal, METH_VARARGS},
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O,
PyDoc_STR("set_error_class(error_class) -> None")},
{"pymarshal_write_long_to_file",

View File

@ -66,6 +66,39 @@ signal_pause(PyObject *module, PyObject *Py_UNUSED(ignored))
#endif /* defined(HAVE_PAUSE) */
PyDoc_STRVAR(signal_raise_signal__doc__,
"raise_signal($module, signalnum, /)\n"
"--\n"
"\n"
"Send a signal to the executing process.");
#define SIGNAL_RAISE_SIGNAL_METHODDEF \
{"raise_signal", (PyCFunction)signal_raise_signal, METH_O, signal_raise_signal__doc__},
static PyObject *
signal_raise_signal_impl(PyObject *module, int signalnum);
static PyObject *
signal_raise_signal(PyObject *module, PyObject *arg)
{
PyObject *return_value = NULL;
int signalnum;
if (PyFloat_Check(arg)) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
signalnum = _PyLong_AsInt(arg);
if (signalnum == -1 && PyErr_Occurred()) {
goto exit;
}
return_value = signal_raise_signal_impl(module, signalnum);
exit:
return return_value;
}
PyDoc_STRVAR(signal_signal__doc__,
"signal($module, signalnum, handler, /)\n"
"--\n"
@ -558,4 +591,4 @@ exit:
#ifndef SIGNAL_PTHREAD_KILL_METHODDEF
#define SIGNAL_PTHREAD_KILL_METHODDEF
#endif /* !defined(SIGNAL_PTHREAD_KILL_METHODDEF) */
/*[clinic end generated code: output=4ed8c36860f9f577 input=a9049054013a1b77]*/
/*[clinic end generated code: output=365db4e807c26d4e input=a9049054013a1b77]*/

View File

@ -390,6 +390,31 @@ signal_pause_impl(PyObject *module)
#endif
/*[clinic input]
signal.raise_signal
signalnum: int
/
Send a signal to the executing process.
[clinic start generated code]*/
static PyObject *
signal_raise_signal_impl(PyObject *module, int signalnum)
/*[clinic end generated code: output=e2b014220aa6111d input=e90c0f9a42358de6]*/
{
int err;
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
err = raise(signalnum);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (err) {
return PyErr_SetFromErrno(PyExc_OSError);
}
Py_RETURN_NONE;
}
/*[clinic input]
signal.signal
@ -1208,6 +1233,7 @@ static PyMethodDef signal_methods[] = {
SIGNAL_SETITIMER_METHODDEF
SIGNAL_GETITIMER_METHODDEF
SIGNAL_SIGNAL_METHODDEF
SIGNAL_RAISE_SIGNAL_METHODDEF
SIGNAL_STRSIGNAL_METHODDEF
SIGNAL_GETSIGNAL_METHODDEF
{"set_wakeup_fd", (PyCFunction)(void(*)(void))signal_set_wakeup_fd, METH_VARARGS | METH_KEYWORDS, set_wakeup_fd_doc},