Commit 608876b6 authored by Matěj Cepl's avatar Matěj Cepl Committed by Antoine Pitrou

bpo-23395: Fix PyErr_SetInterrupt if the SIGINT signal is ignored or not handled (GH-7778)

``_thread.interrupt_main()`` now avoids setting the Python error status if the ``SIGINT`` signal is ignored or not handled by Python.
parent b82e17e6
...@@ -519,13 +519,13 @@ Signal Handling ...@@ -519,13 +519,13 @@ Signal Handling
single: SIGINT single: SIGINT
single: KeyboardInterrupt (built-in exception) single: KeyboardInterrupt (built-in exception)
This function simulates the effect of a :const:`SIGINT` signal arriving --- the Simulate the effect of a :const:`SIGINT` signal arriving. The next time
next time :c:func:`PyErr_CheckSignals` is called, :exc:`KeyboardInterrupt` will :c:func:`PyErr_CheckSignals` is called, the Python signal handler for
be raised. It may be called without holding the interpreter lock. :const:`SIGINT` will be called.
.. % XXX This was described as obsolete, but is used in
.. % _thread.interrupt_main() (used from IDLE), so it's still needed.
If :const:`SIGINT` isn't handled by Python (it was set to
:data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), this function does
nothing.
.. c:function:: int PySignal_SetWakeupFd(int fd) .. c:function:: int PySignal_SetWakeupFd(int fd)
......
...@@ -53,8 +53,12 @@ This module defines the following constants and functions: ...@@ -53,8 +53,12 @@ This module defines the following constants and functions:
.. function:: interrupt_main() .. function:: interrupt_main()
Raise a :exc:`KeyboardInterrupt` exception in the main thread. A subthread can Simulate the effect of a :data:`signal.SIGINT` signal arriving in the main
use this function to interrupt the main thread. thread. A thread can use this function to interrupt the main thread.
If :data:`signal.SIGINT` isn't handled by Python (it was set to
:data:`signal.SIG_DFL` or :data:`signal.SIG_IGN`), this function does
nothing.
.. function:: exit() .. function:: exit()
......
...@@ -16,6 +16,7 @@ import unittest ...@@ -16,6 +16,7 @@ import unittest
import weakref import weakref
import os import os
import subprocess import subprocess
import signal
from test import lock_tests from test import lock_tests
from test import support from test import support
...@@ -1168,6 +1169,7 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): ...@@ -1168,6 +1169,7 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
class BarrierTests(lock_tests.BarrierTests): class BarrierTests(lock_tests.BarrierTests):
barriertype = staticmethod(threading.Barrier) barriertype = staticmethod(threading.Barrier)
class MiscTestCase(unittest.TestCase): class MiscTestCase(unittest.TestCase):
def test__all__(self): def test__all__(self):
extra = {"ThreadError"} extra = {"ThreadError"}
...@@ -1175,5 +1177,38 @@ class MiscTestCase(unittest.TestCase): ...@@ -1175,5 +1177,38 @@ class MiscTestCase(unittest.TestCase):
support.check__all__(self, threading, ('threading', '_thread'), support.check__all__(self, threading, ('threading', '_thread'),
extra=extra, blacklist=blacklist) extra=extra, blacklist=blacklist)
class InterruptMainTests(unittest.TestCase):
def test_interrupt_main_subthread(self):
# Calling start_new_thread with a function that executes interrupt_main
# should raise KeyboardInterrupt upon completion.
def call_interrupt():
_thread.interrupt_main()
t = threading.Thread(target=call_interrupt)
with self.assertRaises(KeyboardInterrupt):
t.start()
t.join()
t.join()
def test_interrupt_main_mainthread(self):
# Make sure that if interrupt_main is called in main thread that
# KeyboardInterrupt is raised instantly.
with self.assertRaises(KeyboardInterrupt):
_thread.interrupt_main()
def test_interrupt_main_noerror(self):
handler = signal.getsignal(signal.SIGINT)
try:
# No exception should arise.
signal.signal(signal.SIGINT, signal.SIG_IGN)
_thread.interrupt_main()
signal.signal(signal.SIGINT, signal.SIG_DFL)
_thread.interrupt_main()
finally:
# Restore original handler
signal.signal(signal.SIGINT, handler)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -261,7 +261,7 @@ Donn Cave ...@@ -261,7 +261,7 @@ Donn Cave
Charles Cazabon Charles Cazabon
Jesús Cea Avión Jesús Cea Avión
Per Cederqvist Per Cederqvist
Matej Cepl Matěj Cepl
Carl Cerecke Carl Cerecke
Octavian Cerna Octavian Cerna
Michael Cetrulo Michael Cetrulo
......
``_thread.interrupt_main()`` now avoids setting the Python error status
if the ``SIGINT`` signal is ignored or not handled by Python.
...@@ -1683,13 +1683,18 @@ _PyErr_CheckSignals(void) ...@@ -1683,13 +1683,18 @@ _PyErr_CheckSignals(void)
} }
/* Replacements for intrcheck.c functionality /* Simulate the effect of a signal.SIGINT signal arriving. The next time
* Declared in pyerrors.h PyErr_CheckSignals is called, the Python SIGINT signal handler will be
*/ raised.
Missing signal handler for the SIGINT signal is silently ignored. */
void void
PyErr_SetInterrupt(void) PyErr_SetInterrupt(void)
{ {
trip_signal(SIGINT); if ((Handlers[SIGINT].func != IgnoreHandler) &&
(Handlers[SIGINT].func != DefaultHandler)) {
trip_signal(SIGINT);
}
} }
void void
......
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