[Python-checkins] bpo-41602: raise SIGINT exit code on KeyboardInterrupt from pymain_run_module (#21956)

Thomas Grainger webhook-mailer at python.org
Tue Sep 22 11:53:12 EDT 2020


https://github.com/python/cpython/commit/a68a2ad19c891faa891904b3da537911cc77df21
commit: a68a2ad19c891faa891904b3da537911cc77df21
branch: master
author: Thomas Grainger <tagrain at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-09-22T08:53:03-07:00
summary:

bpo-41602: raise SIGINT exit code on KeyboardInterrupt from pymain_run_module (#21956)

Closes bpo issue 41602

files:
A Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst
M Lib/test/test_runpy.py
M Modules/main.c

diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py
index f8274a981cb1c..2954dfedc7e42 100644
--- a/Lib/test/test_runpy.py
+++ b/Lib/test/test_runpy.py
@@ -1,15 +1,18 @@
 # Test the runpy module
-import unittest
-import os
+import contextlib
+import importlib.machinery, importlib.util
 import os.path
-import sys
+import pathlib
+import py_compile
 import re
+import signal
+import subprocess
+import sys
 import tempfile
-import importlib, importlib.machinery, importlib.util
-import py_compile
+import textwrap
+import unittest
 import warnings
-import pathlib
-from test.support import verbose, no_tracing
+from test.support import no_tracing, verbose
 from test.support.import_helper import forget, make_legacy_pyc, unload
 from test.support.os_helper import create_empty_file, temp_dir
 from test.support.script_helper import make_script, make_zip_script
@@ -752,5 +755,82 @@ def test_encoding(self):
             self.assertEqual(result['s'], "non-ASCII: h\xe9")
 
 
+class TestExit(unittest.TestCase):
+    STATUS_CONTROL_C_EXIT = 0xC000013A
+    EXPECTED_CODE = (
+        STATUS_CONTROL_C_EXIT
+        if sys.platform == "win32"
+        else -signal.SIGINT
+    )
+    @staticmethod
+    @contextlib.contextmanager
+    def tmp_path(*args, **kwargs):
+        with temp_dir() as tmp_fn:
+            yield pathlib.Path(tmp_fn)
+
+
+    def run(self, *args, **kwargs):
+        with self.tmp_path() as tmp:
+            self.ham = ham = tmp / "ham.py"
+            ham.write_text(
+                textwrap.dedent(
+                    """\
+                    raise KeyboardInterrupt
+                    """
+                )
+            )
+            super().run(*args, **kwargs)
+
+    def assertSigInt(self, *args, **kwargs):
+        proc = subprocess.run(*args, **kwargs, text=True, stderr=subprocess.PIPE)
+        self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n"))
+        self.assertEqual(proc.returncode, self.EXPECTED_CODE)
+
+    def test_pymain_run_file(self):
+        self.assertSigInt([sys.executable, self.ham])
+
+    def test_pymain_run_file_runpy_run_module(self):
+        tmp = self.ham.parent
+        run_module = tmp / "run_module.py"
+        run_module.write_text(
+            textwrap.dedent(
+                """\
+                import runpy
+                runpy.run_module("ham")
+                """
+            )
+        )
+        self.assertSigInt([sys.executable, run_module], cwd=tmp)
+
+    def test_pymain_run_file_runpy_run_module_as_main(self):
+        tmp = self.ham.parent
+        run_module_as_main = tmp / "run_module_as_main.py"
+        run_module_as_main.write_text(
+            textwrap.dedent(
+                """\
+                import runpy
+                runpy._run_module_as_main("ham")
+                """
+            )
+        )
+        self.assertSigInt([sys.executable, run_module_as_main], cwd=tmp)
+
+    def test_pymain_run_command_run_module(self):
+        self.assertSigInt(
+            [sys.executable, "-c", "import runpy; runpy.run_module('ham')"],
+            cwd=self.ham.parent,
+        )
+
+    def test_pymain_run_command(self):
+        self.assertSigInt([sys.executable, "-c", "import ham"], cwd=self.ham.parent)
+
+    def test_pymain_run_stdin(self):
+        self.assertSigInt([sys.executable], input="import ham", cwd=self.ham.parent)
+
+    def test_pymain_run_module(self):
+        ham = self.ham
+        self.assertSigInt([sys.executable, "-m", ham.stem], cwd=ham.parent)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst b/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst
new file mode 100644
index 0000000000000..fa3d2f1aa374e
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2020-08-25-19-25-36.bpo-41602.Z64s0I.rst
@@ -0,0 +1 @@
+Add tests for SIGINT handling in the runpy module.
diff --git a/Modules/main.c b/Modules/main.c
index 4a76f4461bf61..2cc891f61aadd 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -287,7 +287,11 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
         Py_DECREF(module);
         return pymain_exit_err_print();
     }
+    _Py_UnhandledKeyboardInterrupt = 0;
     result = PyObject_Call(runmodule, runargs, NULL);
+    if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) {
+        _Py_UnhandledKeyboardInterrupt = 1;
+    }
     Py_DECREF(runpy);
     Py_DECREF(runmodule);
     Py_DECREF(module);



More information about the Python-checkins mailing list