Skip to content

Commit

Permalink
Fix reporting exit due to signal
Browse files Browse the repository at this point in the history
`subprocess` reports a negative exit code for signals (the assumption in
tox-dev#760 (comment) was
wrong).

TODO:

- [ ] faster test (takes >3s)
  • Loading branch information
blueyed committed Aug 20, 2019
1 parent 3725c93 commit fe85a00
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 10 deletions.
24 changes: 15 additions & 9 deletions src/tox/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ def exit_code_str(exception_name, command, exit_code):
"""
str_ = "{} for command {}".format(exception_name, command)
if exit_code is not None:
str_ += " (exited with code {:d})".format(exit_code)
if (os.name == "posix") and (exit_code > 128):
if (exit_code < 0 or (os.name == "posix" and exit_code > 128)):
signals = {
number: name for name, number in vars(signal).items() if name.startswith("SIG")
}
number = exit_code - 128
name = signals.get(number)
if name:
str_ += (
"\nNote: this might indicate a fatal error signal "
"({:d} - 128 = {:d}: {})".format(number + 128, number, name)
)
if exit_code < 0:
# Signal reported via subprocess.Popen.
sig_name = signals.get(0 - exit_code)
str_ += " (exited with code {:d} ({}))".format(exit_code, sig_name)
else:
str_ += " (exited with code {:d})".format(exit_code)
number = exit_code - 128
name = signals.get(number)
if name:
str_ += (
")\nNote: this might indicate a fatal error signal "
"({:d} - 128 = {:d}: {})".format(exit_code, number, name)
)
str_ += " (exited with code {:d})".format(exit_code)
return str_


Expand Down
37 changes: 37 additions & 0 deletions tests/unit/test_exit_signal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import subprocess
import sys

from tox._pytestplugin import create_files


def test_exit_code_signal(cmd, tmpdir):
create_files(tmpdir, {
# TODO: can this be minified?
"tox.ini": """\
[tox]
skipsdist = true
skips_install = true
[testenv]
commands = {interpreter} tfile.py
""".format(
interpreter=sys.executable
),
"tfile.py": """\
import os, signal
os.kill(os.getpid(), signal.SIGTERM)
""",
})
p = subprocess.Popen([
sys.executable, '-m', 'tox'
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=str(tmpdir))
p.wait()
out = p.stdout.read().decode()
assert p.returncode == 1
assert (
"ERROR: InvocationError for command {} tfile.py (exited with code -15 (SIGTERM))".format(
sys.executable
)
in out
)
assert p.stderr.read() == b""
4 changes: 3 additions & 1 deletion tests/unit/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_get_commandlog(pkg):
assert setuplog2.list == setuplog.list


@pytest.mark.parametrize("exit_code", [None, 0, 5, 128 + signal.SIGTERM, 1234])
@pytest.mark.parametrize("exit_code", [None, 0, 5, 128 + signal.SIGTERM, 1234, -15])
@pytest.mark.parametrize("os_name", ["posix", "nt"])
def test_invocation_error(exit_code, os_name, mocker, monkeypatch):
monkeypatch.setattr(os, "name", value=os_name)
Expand All @@ -85,6 +85,8 @@ def test_invocation_error(exit_code, os_name, mocker, monkeypatch):
assert call_args == mocker.call("InvocationError", "<command>", exit_code)
if exit_code is None:
assert "(exited with code" not in result
elif exit_code is -15:
assert "(exited with code -15 (SIGTERM))" in result
else:
assert "(exited with code %d)" % exit_code in result
note = "Note: this might indicate a fatal error signal"
Expand Down

0 comments on commit fe85a00

Please sign in to comment.