Skip to content

Commit 6debd76

Browse files
committed
Closes issue 15505. unittest.installHandler and non-callable signal handlers.
1 parent 45c4375 commit 6debd76

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

Lib/unittest/signals.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@
99
class _InterruptHandler(object):
1010
def __init__(self, default_handler):
1111
self.called = False
12+
self.original_handler = default_handler
13+
if isinstance(default_handler, int):
14+
if default_handler == signal.SIG_DFL:
15+
# Pretend it's signal.default_int_handler instead.
16+
default_handler = signal.default_int_handler
17+
elif default_handler == signal.SIG_IGN:
18+
# Not quite the same thing as SIG_IGN, but the closest we
19+
# can make it: do nothing.
20+
def default_handler(unused_signum, unused_frame):
21+
pass
22+
else:
23+
raise TypeError("expected SIGINT signal handler to be "
24+
"signal.SIG_IGN, signal.SIG_DFL, or a "
25+
"callable object")
1226
self.default_handler = default_handler
1327

1428
def __call__(self, signum, frame):
@@ -54,4 +68,4 @@ def inner(*args, **kwargs):
5468

5569
global _interrupt_handler
5670
if _interrupt_handler is not None:
57-
signal.signal(signal.SIGINT, _interrupt_handler.default_handler)
71+
signal.signal(signal.SIGINT, _interrupt_handler.original_handler)

Lib/unittest/test/test_break.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
1414
"if threads have been used")
1515
class TestBreak(unittest.TestCase):
16+
int_handler = None
1617

1718
def setUp(self):
1819
self._default_handler = signal.getsignal(signal.SIGINT)
20+
if self.int_handler is not None:
21+
signal.signal(signal.SIGINT, self.int_handler)
1922

2023
def tearDown(self):
2124
signal.signal(signal.SIGINT, self._default_handler)
@@ -72,6 +75,10 @@ def test(result):
7275

7376

7477
def testSecondInterrupt(self):
78+
# Can't use skipIf decorator because the signal handler may have
79+
# been changed after defining this method.
80+
if signal.getsignal(signal.SIGINT) == signal.SIG_IGN:
81+
self.skipTest("test requires SIGINT to not be ignored")
7582
result = unittest.TestResult()
7683
unittest.installHandler()
7784
unittest.registerResult(result)
@@ -121,6 +128,10 @@ def test(result):
121128

122129

123130
def testHandlerReplacedButCalled(self):
131+
# Can't use skipIf decorator because the signal handler may have
132+
# been changed after defining this method.
133+
if signal.getsignal(signal.SIGINT) == signal.SIG_IGN:
134+
self.skipTest("test requires SIGINT to not be ignored")
124135
# If our handler has been replaced (is no longer installed) but is
125136
# called by the *new* handler, then it isn't safe to delay the
126137
# SIGINT and we should immediately delegate to the default handler
@@ -250,3 +261,24 @@ def test():
250261

251262
test()
252263
self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)
264+
265+
@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
266+
@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
267+
@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
268+
"if threads have been used")
269+
class TestBreakDefaultIntHandler(TestBreak):
270+
int_handler = signal.default_int_handler
271+
272+
@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
273+
@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
274+
@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
275+
"if threads have been used")
276+
class TestBreakSignalIgnored(TestBreak):
277+
int_handler = signal.SIG_IGN
278+
279+
@unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
280+
@unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
281+
@unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
282+
"if threads have been used")
283+
class TestBreakSignalDefault(TestBreak):
284+
int_handler = signal.SIG_DFL

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ Core and Builtins
216216
Library
217217
-------
218218

219+
- Issue #15505: `unittest.installHandler` no longer assumes SIGINT handler is
220+
set to a callable object.
221+
219222
- Issue #12004: Fix an internal error in PyZipFile when writing an invalid
220223
Python file. Patch by Ben Morgan.
221224

0 commit comments

Comments
 (0)