Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a68a2ad

Browse filesBrowse files
authored
bpo-41602: raise SIGINT exit code on KeyboardInterrupt from pymain_run_module (#21956)
Closes bpo issue 41602
1 parent ea0711a commit a68a2ad
Copy full SHA for a68a2ad

File tree

3 files changed

+92
-7
lines changed
Filter options

3 files changed

+92
-7
lines changed

‎Lib/test/test_runpy.py

Copy file name to clipboardExpand all lines: Lib/test/test_runpy.py
+87-7Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
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
12-
from test.support import verbose, no_tracing
15+
from test.support import no_tracing, verbose
1316
from test.support.import_helper import forget, make_legacy_pyc, unload
1417
from test.support.os_helper import create_empty_file, temp_dir
1518
from test.support.script_helper import make_script, make_zip_script
@@ -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()
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add tests for SIGINT handling in the runpy module.

‎Modules/main.c

Copy file name to clipboardExpand all lines: Modules/main.c
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,11 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
287287
Py_DECREF(module);
288288
return pymain_exit_err_print();
289289
}
290+
_Py_UnhandledKeyboardInterrupt = 0;
290291
result = PyObject_Call(runmodule, runargs, NULL);
292+
if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) {
293+
_Py_UnhandledKeyboardInterrupt = 1;
294+
}
291295
Py_DECREF(runpy);
292296
Py_DECREF(runmodule);
293297
Py_DECREF(module);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.