summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_signal.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_signal.py')
-rw-r--r--Lib/test/test_signal.py61
1 files changed, 57 insertions, 4 deletions
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index 2a6217e..80c0ff4 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -78,6 +78,48 @@ class PosixTests(unittest.TestCase):
self.assertNotIn(signal.NSIG, s)
self.assertLess(len(s), signal.NSIG)
+ @unittest.skipUnless(sys.executable, "sys.executable required.")
+ def test_keyboard_interrupt_exit_code(self):
+ """KeyboardInterrupt triggers exit via SIGINT."""
+ process = subprocess.run(
+ [sys.executable, "-c",
+ "import os,signal; os.kill(os.getpid(), signal.SIGINT)"],
+ stderr=subprocess.PIPE)
+ self.assertIn(b"KeyboardInterrupt", process.stderr)
+ self.assertEqual(process.returncode, -signal.SIGINT)
+
+ @unittest.skipUnless(sys.executable, "sys.executable required.")
+ def test_keyboard_interrupt_communicated_to_shell(self):
+ """KeyboardInterrupt exits such that shells detect a ^C."""
+ try:
+ bash_proc = subprocess.run(
+ ["bash", "-c", 'echo "${BASH_VERSION}"'],
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
+ except OSError:
+ raise unittest.SkipTest("bash required.")
+ if bash_proc.returncode:
+ raise unittest.SkipTest("could not determine bash version.")
+ bash_ver = bash_proc.stdout.decode("ascii").strip()
+ bash_major_minor = [int(n) for n in bash_ver.split(".", 2)[:2]]
+ if bash_major_minor < [4, 4]:
+ # In older versions of bash, -i does not work as needed
+ # _for this automated test_. Older shells do behave as
+ # expected in manual interactive use.
+ raise unittest.SkipTest(f"bash version {bash_ver} is too old.")
+ # The motivation for https://bugs.python.org/issue1054041.
+ # An _interactive_ shell (bash -i simulates that here) detects
+ # when a command exits via ^C and stops executing further
+ # commands.
+ process = subprocess.run(
+ ["bash", "-ic",
+ f"{sys.executable} -c 'import os,signal; os.kill(os.getpid(), signal.SIGINT)'; "
+ "echo TESTFAIL using bash \"${BASH_VERSION}\""],
+ stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ self.assertIn(b"KeyboardInterrupt", process.stderr)
+ # An interactive shell will abort if python exits properly to
+ # indicate that a KeyboardInterrupt occurred.
+ self.assertNotIn(b"TESTFAIL", process.stdout)
+
@unittest.skipUnless(sys.platform == "win32", "Windows specific")
class WindowsSignalTests(unittest.TestCase):
@@ -112,6 +154,20 @@ class WindowsSignalTests(unittest.TestCase):
with self.assertRaises(ValueError):
signal.signal(7, handler)
+ @unittest.skipUnless(sys.executable, "sys.executable required.")
+ def test_keyboard_interrupt_exit_code(self):
+ """KeyboardInterrupt triggers an exit using STATUS_CONTROL_C_EXIT."""
+ # We don't test via os.kill(os.getpid(), signal.CTRL_C_EVENT) here
+ # as that requires setting up a console control handler in a child
+ # in its own process group. Doable, but quite complicated. (see
+ # @eryksun on https://github.com/python/cpython/pull/11862)
+ process = subprocess.run(
+ [sys.executable, "-c", "raise KeyboardInterrupt"],
+ stderr=subprocess.PIPE)
+ self.assertIn(b"KeyboardInterrupt", process.stderr)
+ STATUS_CONTROL_C_EXIT = 0xC000013A
+ self.assertEqual(process.returncode, STATUS_CONTROL_C_EXIT)
+
class WakeupFDTests(unittest.TestCase):
@@ -1217,11 +1273,8 @@ class StressTest(unittest.TestCase):
class RaiseSignalTest(unittest.TestCase):
def test_sigint(self):
- try:
+ with self.assertRaises(KeyboardInterrupt):
signal.raise_signal(signal.SIGINT)
- self.fail("Expected KeyInterrupt")
- except KeyboardInterrupt:
- pass
@unittest.skipIf(sys.platform != "win32", "Windows specific test")
def test_invalid_argument(self):