From 6debd769393c9c5cdd4feb2a0c74bda173a13b1e Mon Sep 17 00:00:00 2001 From: Michael Foord Date: Tue, 29 Jan 2013 23:07:57 +0000 Subject: Closes issue 15505. unittest.installHandler and non-callable signal handlers. --- Lib/unittest/signals.py | 16 +++++++++++++++- Lib/unittest/test/test_break.py | 32 ++++++++++++++++++++++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Lib/unittest/signals.py b/Lib/unittest/signals.py index fc31043..e6a5fc5 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 77ce201..75532f4 100644 --- a/Lib/unittest/test/test_break.py +++ b/Lib/unittest/test/test_break.py @@ -13,9 +13,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) @@ -72,6 +75,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) @@ -121,6 +128,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 7ebe3f8..e9cd8c7 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -216,6 +216,9 @@ Core and Builtins Library ------- +- Issue #15505: `unittest.installHandler` no longer assumes SIGINT handler is + set to a callable object. + - Issue #12004: Fix an internal error in PyZipFile when writing an invalid Python file. Patch by Ben Morgan. -- cgit v0.12