diff --git a/Lib/unittest/signals.py b/Lib/unittest/signals.py index fc31043283b..e6a5fc52439 100644 --- a/Lib/unittest/signals.py +++ b/Lib/unittest/signals.py @@ -9,6 +9,20 @@ __unittest = True class _InterruptHandler(object): def __init__(self, default_handler): self.called = False + self.original_handler = default_handler + if isinstance(default_handler, int): + if default_handler == signal.SIG_DFL: + # Pretend it's signal.default_int_handler instead. + default_handler = signal.default_int_handler + elif default_handler == signal.SIG_IGN: + # Not quite the same thing as SIG_IGN, but the closest we + # can make it: do nothing. + def default_handler(unused_signum, unused_frame): + pass + else: + raise TypeError("expected SIGINT signal handler to be " + "signal.SIG_IGN, signal.SIG_DFL, or a " + "callable object") self.default_handler = default_handler def __call__(self, signum, frame): @@ -54,4 +68,4 @@ def removeHandler(method=None): global _interrupt_handler if _interrupt_handler is not None: - signal.signal(signal.SIGINT, _interrupt_handler.default_handler) + signal.signal(signal.SIGINT, _interrupt_handler.original_handler) diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py index 560001103f8..dab91c135c6 100644 --- a/Lib/unittest/test/test_break.py +++ b/Lib/unittest/test/test_break.py @@ -15,9 +15,12 @@ import unittest @unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 " "if threads have been used") class TestBreak(unittest.TestCase): + int_handler = None def setUp(self): self._default_handler = signal.getsignal(signal.SIGINT) + if self.int_handler is not None: + signal.signal(signal.SIGINT, self.int_handler) def tearDown(self): signal.signal(signal.SIGINT, self._default_handler) @@ -74,6 +77,10 @@ class TestBreak(unittest.TestCase): def testSecondInterrupt(self): + # Can't use skipIf decorator because the signal handler may have + # been changed after defining this method. + if signal.getsignal(signal.SIGINT) == signal.SIG_IGN: + self.skipTest("test requires SIGINT to not be ignored") result = unittest.TestResult() unittest.installHandler() unittest.registerResult(result) @@ -123,6 +130,10 @@ class TestBreak(unittest.TestCase): def testHandlerReplacedButCalled(self): + # Can't use skipIf decorator because the signal handler may have + # been changed after defining this method. + if signal.getsignal(signal.SIGINT) == signal.SIG_IGN: + self.skipTest("test requires SIGINT to not be ignored") # If our handler has been replaced (is no longer installed) but is # called by the *new* handler, then it isn't safe to delay the # SIGINT and we should immediately delegate to the default handler @@ -250,3 +261,24 @@ class TestBreak(unittest.TestCase): test() self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) + +@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") +@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") +@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 " + "if threads have been used") +class TestBreakDefaultIntHandler(TestBreak): + int_handler = signal.default_int_handler + +@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") +@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") +@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 " + "if threads have been used") +class TestBreakSignalIgnored(TestBreak): + int_handler = signal.SIG_IGN + +@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") +@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") +@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 " + "if threads have been used") +class TestBreakSignalDefault(TestBreak): + int_handler = signal.SIG_DFL diff --git a/Misc/NEWS b/Misc/NEWS index 89cf9d39c18..8057afe015c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -202,6 +202,9 @@ Core and Builtins Library ------- +- Issue #15505: `unittest.installHandler` no longer assumes SIGINT handler is + set to a callable object. + - Issue #17051: Fix a memory leak in os.path.isdir() on Windows. Patch by Robert Xiao.