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.
This commit is contained in:
parent
b82e17e626
commit
608876b6b1
|
@ -519,13 +519,13 @@ Signal Handling
|
|||
single: SIGINT
|
||||
single: KeyboardInterrupt (built-in exception)
|
||||
|
||||
This function simulates the effect of a :const:`SIGINT` signal arriving --- the
|
||||
next time :c:func:`PyErr_CheckSignals` is called, :exc:`KeyboardInterrupt` will
|
||||
be raised. It may be called without holding the interpreter lock.
|
||||
|
||||
.. % XXX This was described as obsolete, but is used in
|
||||
.. % _thread.interrupt_main() (used from IDLE), so it's still needed.
|
||||
Simulate the effect of a :const:`SIGINT` signal arriving. The next time
|
||||
:c:func:`PyErr_CheckSignals` is called, the Python signal handler for
|
||||
:const:`SIGINT` will be called.
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -53,8 +53,12 @@ This module defines the following constants and functions:
|
|||
|
||||
.. function:: interrupt_main()
|
||||
|
||||
Raise a :exc:`KeyboardInterrupt` exception in the main thread. A subthread can
|
||||
use this function to interrupt the main thread.
|
||||
Simulate the effect of a :data:`signal.SIGINT` signal arriving in the main
|
||||
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()
|
||||
|
|
|
@ -16,6 +16,7 @@ import unittest
|
|||
import weakref
|
||||
import os
|
||||
import subprocess
|
||||
import signal
|
||||
|
||||
from test import lock_tests
|
||||
from test import support
|
||||
|
@ -1168,6 +1169,7 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
|
|||
class BarrierTests(lock_tests.BarrierTests):
|
||||
barriertype = staticmethod(threading.Barrier)
|
||||
|
||||
|
||||
class MiscTestCase(unittest.TestCase):
|
||||
def test__all__(self):
|
||||
extra = {"ThreadError"}
|
||||
|
@ -1175,5 +1177,38 @@ class MiscTestCase(unittest.TestCase):
|
|||
support.check__all__(self, threading, ('threading', '_thread'),
|
||||
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__":
|
||||
unittest.main()
|
||||
|
|
|
@ -261,7 +261,7 @@ Donn Cave
|
|||
Charles Cazabon
|
||||
Jesús Cea Avión
|
||||
Per Cederqvist
|
||||
Matej Cepl
|
||||
Matěj Cepl
|
||||
Carl Cerecke
|
||||
Octavian Cerna
|
||||
Michael Cetrulo
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
``_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)
|
|||
}
|
||||
|
||||
|
||||
/* Replacements for intrcheck.c functionality
|
||||
* Declared in pyerrors.h
|
||||
*/
|
||||
/* Simulate the effect of a signal.SIGINT signal arriving. The next time
|
||||
PyErr_CheckSignals is called, the Python SIGINT signal handler will be
|
||||
raised.
|
||||
|
||||
Missing signal handler for the SIGINT signal is silently ignored. */
|
||||
void
|
||||
PyErr_SetInterrupt(void)
|
||||
{
|
||||
trip_signal(SIGINT);
|
||||
if ((Handlers[SIGINT].func != IgnoreHandler) &&
|
||||
(Handlers[SIGINT].func != DefaultHandler)) {
|
||||
trip_signal(SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in New Issue