Closes issue 15505. unittest.installHandler and non-callable signal handlers.

This commit is contained in:
Michael Foord 2013-01-29 23:07:57 +00:00
parent 45c4375ea7
commit 6debd76939
3 changed files with 50 additions and 1 deletions

View File

@ -9,6 +9,20 @@ __unittest = True
class _InterruptHandler(object): class _InterruptHandler(object):
def __init__(self, default_handler): def __init__(self, default_handler):
self.called = False 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 self.default_handler = default_handler
def __call__(self, signum, frame): def __call__(self, signum, frame):
@ -54,4 +68,4 @@ def removeHandler(method=None):
global _interrupt_handler global _interrupt_handler
if _interrupt_handler is not None: if _interrupt_handler is not None:
signal.signal(signal.SIGINT, _interrupt_handler.default_handler) signal.signal(signal.SIGINT, _interrupt_handler.original_handler)

View File

@ -13,9 +13,12 @@ import unittest
@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 " @unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
"if threads have been used") "if threads have been used")
class TestBreak(unittest.TestCase): class TestBreak(unittest.TestCase):
int_handler = None
def setUp(self): def setUp(self):
self._default_handler = signal.getsignal(signal.SIGINT) self._default_handler = signal.getsignal(signal.SIGINT)
if self.int_handler is not None:
signal.signal(signal.SIGINT, self.int_handler)
def tearDown(self): def tearDown(self):
signal.signal(signal.SIGINT, self._default_handler) signal.signal(signal.SIGINT, self._default_handler)
@ -72,6 +75,10 @@ class TestBreak(unittest.TestCase):
def testSecondInterrupt(self): 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() result = unittest.TestResult()
unittest.installHandler() unittest.installHandler()
unittest.registerResult(result) unittest.registerResult(result)
@ -121,6 +128,10 @@ class TestBreak(unittest.TestCase):
def testHandlerReplacedButCalled(self): 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 # 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 # called by the *new* handler, then it isn't safe to delay the
# SIGINT and we should immediately delegate to the default handler # SIGINT and we should immediately delegate to the default handler
@ -250,3 +261,24 @@ class TestBreak(unittest.TestCase):
test() test()
self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) 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

View File

@ -216,6 +216,9 @@ Core and Builtins
Library 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 - Issue #12004: Fix an internal error in PyZipFile when writing an invalid
Python file. Patch by Ben Morgan. Python file. Patch by Ben Morgan.