Skip to content

Commit cccc27a

Browse files
committed
[3.8] bpo-41602: raise SIGINT exit code on KeyboardInterrupt from pymain_run_module (GH-21956)
Closes bpo issue 41602. (cherry picked from commit a68a2ad) Co-authored-by: Thomas Grainger <tagrain@gmail.com>
1 parent 87ca11b commit cccc27a

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

Lib/test/test_runpy.py

+86-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# Test the runpy module
2-
import unittest
3-
import os
2+
import contextlib
3+
import importlib.machinery, importlib.util
44
import os.path
5-
import sys
5+
import pathlib
6+
import py_compile
67
import re
8+
import signal
9+
import subprocess
10+
import sys
711
import tempfile
8-
import importlib, importlib.machinery, importlib.util
9-
import py_compile
12+
import textwrap
13+
import unittest
1014
import warnings
11-
import pathlib
1215
from test.support import (
1316
forget, make_legacy_pyc, unload, verbose, no_tracing,
1417
create_empty_file, temp_dir)
@@ -752,5 +755,82 @@ def test_encoding(self):
752755
self.assertEqual(result['s'], "non-ASCII: h\xe9")
753756

754757

758+
class TestExit(unittest.TestCase):
759+
STATUS_CONTROL_C_EXIT = 0xC000013A
760+
EXPECTED_CODE = (
761+
STATUS_CONTROL_C_EXIT
762+
if sys.platform == "win32"
763+
else -signal.SIGINT
764+
)
765+
@staticmethod
766+
@contextlib.contextmanager
767+
def tmp_path(*args, **kwargs):
768+
with temp_dir() as tmp_fn:
769+
yield pathlib.Path(tmp_fn)
770+
771+
772+
def run(self, *args, **kwargs):
773+
with self.tmp_path() as tmp:
774+
self.ham = ham = tmp / "ham.py"
775+
ham.write_text(
776+
textwrap.dedent(
777+
"""\
778+
raise KeyboardInterrupt
779+
"""
780+
)
781+
)
782+
super().run(*args, **kwargs)
783+
784+
def assertSigInt(self, *args, **kwargs):
785+
proc = subprocess.run(*args, **kwargs, text=True, stderr=subprocess.PIPE)
786+
self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n"))
787+
self.assertEqual(proc.returncode, self.EXPECTED_CODE)
788+
789+
def test_pymain_run_file(self):
790+
self.assertSigInt([sys.executable, self.ham])
791+
792+
def test_pymain_run_file_runpy_run_module(self):
793+
tmp = self.ham.parent
794+
run_module = tmp / "run_module.py"
795+
run_module.write_text(
796+
textwrap.dedent(
797+
"""\
798+
import runpy
799+
runpy.run_module("ham")
800+
"""
801+
)
802+
)
803+
self.assertSigInt([sys.executable, run_module], cwd=tmp)
804+
805+
def test_pymain_run_file_runpy_run_module_as_main(self):
806+
tmp = self.ham.parent
807+
run_module_as_main = tmp / "run_module_as_main.py"
808+
run_module_as_main.write_text(
809+
textwrap.dedent(
810+
"""\
811+
import runpy
812+
runpy._run_module_as_main("ham")
813+
"""
814+
)
815+
)
816+
self.assertSigInt([sys.executable, run_module_as_main], cwd=tmp)
817+
818+
def test_pymain_run_command_run_module(self):
819+
self.assertSigInt(
820+
[sys.executable, "-c", "import runpy; runpy.run_module('ham')"],
821+
cwd=self.ham.parent,
822+
)
823+
824+
def test_pymain_run_command(self):
825+
self.assertSigInt([sys.executable, "-c", "import ham"], cwd=self.ham.parent)
826+
827+
def test_pymain_run_stdin(self):
828+
self.assertSigInt([sys.executable], input="import ham", cwd=self.ham.parent)
829+
830+
def test_pymain_run_module(self):
831+
ham = self.ham
832+
self.assertSigInt([sys.executable, "-m", ham.stem], cwd=ham.parent)
833+
834+
755835
if __name__ == "__main__":
756836
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add tests for SIGINT handling in the runpy module.

Modules/main.c

+4
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,11 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
299299
Py_DECREF(module);
300300
return pymain_exit_err_print();
301301
}
302+
_Py_UnhandledKeyboardInterrupt = 0;
302303
result = PyObject_Call(runmodule, runargs, NULL);
304+
if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) {
305+
_Py_UnhandledKeyboardInterrupt = 1;
306+
}
303307
Py_DECREF(runpy);
304308
Py_DECREF(runmodule);
305309
Py_DECREF(module);

0 commit comments

Comments
 (0)