[3.9] bpo-41602: raise SIGINT exit code on KeyboardInterrupt from pymain_run_module (GH-21956) (#22397)

Closes bpo issue 41602.
(cherry picked from commit a68a2ad19c)

Co-authored-by: Thomas Grainger <tagrain@gmail.com>
This commit is contained in:
Łukasz Langa 2020-09-24 16:34:21 +02:00 committed by GitHub
parent 57e7d5c19e
commit ca8d46dd42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 8 deletions

View File

@ -1,14 +1,17 @@
# Test the runpy module # Test the runpy module
import unittest import contextlib
import os import importlib.machinery, importlib.util
import os.path import os.path
import sys
import re
import tempfile
import importlib, importlib.machinery, importlib.util
import py_compile
import warnings
import pathlib import pathlib
import py_compile
import re
import signal
import subprocess
import sys
import tempfile
import textwrap
import unittest
import warnings
from test.support import ( from test.support import (
forget, make_legacy_pyc, unload, verbose, no_tracing, forget, make_legacy_pyc, unload, verbose, no_tracing,
create_empty_file, temp_dir) create_empty_file, temp_dir)
@ -752,5 +755,82 @@ s = "non-ASCII: h\xe9"
self.assertEqual(result['s'], "non-ASCII: h\xe9") 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -0,0 +1 @@
Add tests for SIGINT handling in the runpy module.

View File

@ -287,7 +287,11 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
Py_DECREF(module); Py_DECREF(module);
return pymain_exit_err_print(); return pymain_exit_err_print();
} }
_Py_UnhandledKeyboardInterrupt = 0;
result = PyObject_Call(runmodule, runargs, NULL); result = PyObject_Call(runmodule, runargs, NULL);
if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) {
_Py_UnhandledKeyboardInterrupt = 1;
}
Py_DECREF(runpy); Py_DECREF(runpy);
Py_DECREF(runmodule); Py_DECREF(runmodule);
Py_DECREF(module); Py_DECREF(module);