##// END OF EJS Templates
[issue6883] catch keyboardinterrupt if generated during shell.system() calls
mvr -
Show More
@@ -2355,19 +2355,31 b' class InteractiveShell(SingletonConfigurable):'
2355 ec = os.system(cmd)
2355 ec = os.system(cmd)
2356 else:
2356 else:
2357 cmd = py3compat.unicode_to_str(cmd)
2357 cmd = py3compat.unicode_to_str(cmd)
2358 # Call the cmd using the OS shell, instead of the default /bin/sh, if set.
2358 # For posix the result of the subprocess.call() below is an exit
2359 ec = subprocess.call(cmd, shell=True, executable=os.environ.get('SHELL', None))
2359 # code, which by convention is zero for success, positive for
2360 # exit code is positive for program failure, or negative for
2360 # program failure. Exit codes above 128 are reserved for signals,
2361 # terminating signal number.
2361 # and the formula for converting a signal to an exit code is usually
2362
2362 # signal_number+128. To more easily differentiate between exit
2363 # Interpret ec > 128 as signal
2363 # codes and signals, ipython uses negative numbers. For instance
2364 # Some shells (csh, fish) don't follow sh/bash conventions for exit codes
2364 # since control-c is signal 2 but exit code 130, ipython's
2365 # _exit_code variable will read -2. Note that some shells like
2366 # csh and fish don't follow sh/bash conventions for exit codes.
2367 executable = os.environ.get('SHELL', None)
2368 try:
2369 # Use env shell instead of default /bin/sh
2370 ec = subprocess.call(cmd, shell=True, executable=executable)
2371 except KeyboardInterrupt:
2372 # intercept control-C; a long traceback is not useful here
2373 self.write_err("\nKeyboardInterrupt\n")
2374 ec = 130
2365 if ec > 128:
2375 if ec > 128:
2366 ec = -(ec - 128)
2376 ec = -(ec - 128)
2367
2377
2368 # We explicitly do NOT return the subprocess status code, because
2378 # We explicitly do NOT return the subprocess status code, because
2369 # a non-None value would trigger :func:`sys.displayhook` calls.
2379 # a non-None value would trigger :func:`sys.displayhook` calls.
2370 # Instead, we store the exit_code in user_ns.
2380 # Instead, we store the exit_code in user_ns. Note the semantics
2381 # of _exit_code: for control-c, _exit_code == -signal.SIGNIT,
2382 # but raising SystemExit(_exit_code) will give status 254!
2371 self.user_ns['_exit_code'] = ec
2383 self.user_ns['_exit_code'] = ec
2372
2384
2373 # use piped system by default, because it is better behaved
2385 # use piped system by default, because it is better behaved
@@ -544,6 +544,16 b' class TestSystemRaw(unittest.TestCase, ExitCodeChecks):'
544 cmd = u'''python -c "'åäö'" '''
544 cmd = u'''python -c "'åäö'" '''
545 ip.system_raw(cmd)
545 ip.system_raw(cmd)
546
546
547 @mock.patch('subprocess.call')
548 def test_control_c(self, call_mock):
549 call_mock.side_effect = KeyboardInterrupt()
550 try:
551 self.system("sleep 1 # wont happen")
552 except KeyboardInterrupt:
553 self.fail("system call should intercept "
554 "keyboard interrupt from subprocess.call")
555 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
556
547 # TODO: Exit codes are currently ignored on Windows.
557 # TODO: Exit codes are currently ignored on Windows.
548 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
558 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
549 system = ip.system_piped
559 system = ip.system_piped
General Comments 0
You need to be logged in to leave comments. Login now