Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (revision 87729) +++ Python/pythonrun.c (working copy) @@ -1691,6 +1691,12 @@ mod = PyParser_ASTFromString(str, "", start, flags, arena); if (mod != NULL) ret = run_mod(mod, "", globals, locals, flags, arena); + /* TODO(gps): issue1054041: check for an uncaught KeyboardInterrupt * + * exception and if so, set a flag so that our caller can check it and * + * SIGINT ourselves instead of exiting normally. */ + if (!ret && PyErr_Occurred() == PyExc_KeyboardInterrupt) { + _Py_UnhandledKeyboardInterrupt = 1; + } PyArena_Free(arena); return ret; } @@ -1714,6 +1720,12 @@ return NULL; } ret = run_mod(mod, filename, globals, locals, flags, arena); + /* TODO(gps): issue1054041: check for an uncaught KeyboardInterrupt * + * exception and if so, set a flag so that our caller can check it and * + * SIGINT ourselves instead of exiting normally. */ + if (!ret && PyErr_Occurred() == PyExc_KeyboardInterrupt) { + _Py_UnhandledKeyboardInterrupt = 1; + } PyArena_Free(arena); return ret; } Index: Include/pyerrors.h =================================================================== --- Include/pyerrors.h (revision 87729) +++ Include/pyerrors.h (working copy) @@ -175,6 +175,10 @@ PyAPI_DATA(PyObject *) PyExc_ResourceWarning; +/* A flag to indicate the particular way in which Py_Main() exited */ +PyAPI_DATA(int) _Py_UnhandledKeyboardInterrupt; + + /* Convenience functions */ PyAPI_FUNC(int) PyErr_BadArgument(void); Index: Lib/test/test_sigint_exit.py =================================================================== --- Lib/test/test_sigint_exit.py (revision 0) +++ Lib/test/test_sigint_exit.py (revision 0) @@ -0,0 +1,27 @@ +import unittest +from test import support +import subprocess +import sys +import signal + + +class KeyboardInterruptTestCase(unittest.TestCase): + def testKeyboardInterruptSignalExit(self): + """issue1054041: uncaught SIGINT should terminate via the signal.""" + p = subprocess.Popen([sys.executable, '-c', 'raise KeyboardInterrupt'], + stderr=subprocess.PIPE) + stderr = p.stderr.read() + p.stderr.close() + returncode = p.wait() + self.assertEqual(returncode, -signal.SIGINT, + "not a SIGINT exit code. process stderr:\n%s" % stderr) + + +def test_main(): + unit_tests = (KeyboardInterruptTestCase,) + support.run_unittest(*unit_tests) + support.reap_children() + + +if __name__ == "__main__": + test_main() Index: Modules/python.c =================================================================== --- Modules/python.c (revision 87729) +++ Modules/python.c (working copy) @@ -62,6 +62,12 @@ } PyMem_Free(argv_copy); PyMem_Free(argv_copy2); + /* issue1054041: escaping KeyboardInterrupt means die via to SIGINT. */ + if (res != 0 && _Py_UnhandledKeyboardInterrupt) { + signal(SIGINT, SIG_DFL); + kill(getpid(), SIGINT); + assert("SIGINT should have killed us.\n" == NULL); + } return res; } #endif Index: Modules/main.c =================================================================== --- Modules/main.c (revision 87729) +++ Modules/main.c (working copy) @@ -41,6 +41,9 @@ extern "C" { #endif +/* A flag to tell Py_Main()'s caller that KeyboardInterrupt caused our exit. */ +int _Py_UnhandledKeyboardInterrupt = 0; + /* For Py_GetArgcArgv(); set by main() */ static wchar_t **orig_argv; static int orig_argc;