summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Diaz Barquero <dajose22@gmail.com>2021-01-20 02:05:50 -0600
committerGitHub <noreply@github.com>2021-01-20 08:05:50 +0000
commit833b33f84c02ee7e142d4fad5fc00f1120a3c1e2 (patch)
treeb23420a913e507c84e46f3bf79bd1071ae222f4e
parent3d80588df8ba2e3a382b4345d2bf6cea44d3f901 (diff)
downloadtox-git-833b33f84c02ee7e142d4fad5fc00f1120a3c1e2.tar.gz
Handle sigterm by killing the commands subprocess (#1860)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
-rw-r--r--docs/changelog/1772.bugfix.rst2
-rw-r--r--docs/config.rst6
-rw-r--r--src/tox/action.py13
-rw-r--r--tests/integration/test_provision_int.py5
4 files changed, 21 insertions, 5 deletions
diff --git a/docs/changelog/1772.bugfix.rst b/docs/changelog/1772.bugfix.rst
new file mode 100644
index 00000000..c96ba979
--- /dev/null
+++ b/docs/changelog/1772.bugfix.rst
@@ -0,0 +1,2 @@
+Fix a killed tox (via SIGTERM) leaving the commands subprocesses running
+by handling it as if it were a KeyboardInterrupt - by :user:`dajose`
diff --git a/docs/config.rst b/docs/config.rst
index f3bb1b12..01bc9b7d 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -607,9 +607,9 @@ Complete list of settings that you can put into ``testenv*`` sections:
.. versionadded:: 3.15.2
- When an interrupt is sent via Ctrl+C, the SIGINT is sent to all foreground
- processes. The :conf:``suicide_timeout`` gives the running process time to
- cleanup and exit before receiving (in some cases, a duplicate) SIGINT from
+ When an interrupt is sent via Ctrl+C or the tox process is killed with a SIGTERM,
+ a SIGINT is sent to all foreground processes. The :conf:``suicide_timeout`` gives
+ the running process time to cleanup and exit before receiving (in some cases, a duplicate) SIGINT from
tox.
.. conf:: interrupt_timeout ^ float ^ 0.3
diff --git a/src/tox/action.py b/src/tox/action.py
index b5381b83..e7f9b77b 100644
--- a/src/tox/action.py
+++ b/src/tox/action.py
@@ -49,6 +49,10 @@ class Action(object):
self.suicide_timeout = suicide_timeout
self.interrupt_timeout = interrupt_timeout
self.terminate_timeout = terminate_timeout
+ if is_main_thread():
+ # python allows only main thread to install signal handlers
+ # see https://docs.python.org/3/library/signal.html#signals-and-threads
+ self._install_sigterm_handler()
def __enter__(self):
msg = "{} {}".format(self.msg, " ".join(map(str, self.args)))
@@ -278,3 +282,12 @@ class Action(object):
new_args.append(str(arg))
return new_args
+
+ def _install_sigterm_handler(self):
+ """Handle sigterm as if it were a keyboardinterrupt"""
+
+ def sigterm_handler(signum, frame):
+ reporter.error("Got SIGTERM, handling it as a KeyboardInterrupt")
+ raise KeyboardInterrupt()
+
+ signal.signal(signal.SIGTERM, sigterm_handler)
diff --git a/tests/integration/test_provision_int.py b/tests/integration/test_provision_int.py
index 0ae411b8..05fb1a66 100644
--- a/tests/integration/test_provision_int.py
+++ b/tests/integration/test_provision_int.py
@@ -73,7 +73,8 @@ def test_provision_from_pyvenv(initproj, cmd, monkeypatch):
"sys.platform == 'win32'",
reason="triggering SIGINT reliably on Windows is hard",
)
-def test_provision_interrupt_child(initproj, monkeypatch, capfd):
+@pytest.mark.parametrize("signal_type", [signal.SIGINT, signal.SIGTERM])
+def test_provision_interrupt_child(initproj, monkeypatch, capfd, signal_type):
monkeypatch.delenv(str("PYTHONPATH"), raising=False)
monkeypatch.setenv(str("TOX_REPORTER_TIMESTAMP"), str("1"))
initproj(
@@ -123,7 +124,7 @@ def test_provision_interrupt_child(initproj, monkeypatch, capfd):
# 1 process for the host tox, 1 for the provisioned
assert len(all_process) >= 2, all_process
- process.send_signal(signal.CTRL_C_EVENT if sys.platform == "win32" else signal.SIGINT)
+ process.send_signal(signal.CTRL_C_EVENT if sys.platform == "win32" else signal_type)
process.communicate()
out, err = capfd.readouterr()
assert ".tox KeyboardInterrupt: from" in out, out