]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-35568: add 'raise_signal' function (GH-11335)
authorVladimir Matveev <v2matveev@outlook.com>
Tue, 8 Jan 2019 09:58:25 +0000 (01:58 -0800)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 8 Jan 2019 09:58:25 +0000 (01:58 -0800)
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

Doc/library/signal.rst
Lib/test/test_asyncio/test_windows_events.py
Lib/test/test_faulthandler.py
Lib/test/test_posix.py
Lib/test/test_regrtest.py
Lib/test/test_signal.py
Misc/NEWS.d/next/Library/2018-12-27-19-23-00.bpo-35568.PutiOC.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Modules/clinic/signalmodule.c.h
Modules/signalmodule.c

index 5c48c88f08df377a1f23d5d7bf6b78d5d5f8124c..ac6cad9aff8e971a014f7235a27c256e981861b9 100644 (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
index a200a8a80ad9fecf9ff60f46734a87068d652ef8..05f85159be0cd5cbe44c0601cc38f09d350ce158 100644 (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()
index 6aee22be2e037013e962aa6dece21c8784fdc0ed..f0be91844ffa7cba4fb4ab92c55e69b26a076ff2 100644 (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):
index 86c04b9f324a6c790c9919615523842c3bd11d7f..d7e512c99f03d3545532b25ad7593bdfddceba29 100644 (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,
index a67458313addb2b5999afc35c6ec7325c5bfbe82..61937767ec126d4b5080fab702b1b600673a77a5 100644 (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
index b10faa010b2b414ebede73f187097d3b836a1833..2a6217ef64326892a8853755f288c4fd55e13d14 100644 (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()
diff --git a/Misc/NEWS.d/next/Library/2018-12-27-19-23-00.bpo-35568.PutiOC.rst b/Misc/NEWS.d/next/Library/2018-12-27-19-23-00.bpo-35568.PutiOC.rst
new file mode 100644 (file)
index 0000000..d708064
--- /dev/null
@@ -0,0 +1 @@
+Expose ``raise(signum)`` as `raise_signal`
index 4933ef3b61c43b61c580d3c4a8992ff27b329d60..85810f30b1c5289f00c4e8db2e6facfc497cebd3 100644 (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",
index 6745f45ef75105f3dcb1969b0e4a75135415d964..f3742262d8671ddeb3eb9c51f3ac6801cb78ccc6 100644 (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]*/
index 52ab4e998a9778ce419fe64d54321fb21e69bf87..4f8f71a0a1df1337752ec47d6d930738bbac063a 100644 (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},