diff --git a/changelog/4098.feature.rst b/changelog/4098.feature.rst new file mode 100644 index 00000000000..1a53de7597a --- /dev/null +++ b/changelog/4098.feature.rst @@ -0,0 +1 @@ +Add returncode argument to pytest.exit() to exit pytest with a specific return code. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 0171bda0cd3..2d6cea668bf 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -185,10 +185,13 @@ def wrap_session(config, doit): session.exitstatus = EXIT_TESTSFAILED except KeyboardInterrupt: excinfo = _pytest._code.ExceptionInfo() - if initstate < 2 and isinstance(excinfo.value, exit.Exception): + exitstatus = EXIT_INTERRUPTED + if initstate <= 2 and isinstance(excinfo.value, exit.Exception): sys.stderr.write("{}: {}\n".format(excinfo.typename, excinfo.value.msg)) + if excinfo.value.returncode is not None: + exitstatus = excinfo.value.returncode config.hook.pytest_keyboard_interrupt(excinfo=excinfo) - session.exitstatus = EXIT_INTERRUPTED + session.exitstatus = exitstatus except: # noqa excinfo = _pytest._code.ExceptionInfo() config.notify_exception(excinfo, config.option) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index f6093ef7667..4c795838458 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -49,18 +49,24 @@ class Failed(OutcomeException): class Exit(KeyboardInterrupt): """ raised for immediate program exits (no tracebacks/summaries)""" - def __init__(self, msg="unknown reason"): + def __init__(self, msg="unknown reason", returncode=None): self.msg = msg + self.returncode = returncode KeyboardInterrupt.__init__(self, msg) # exposed helper methods -def exit(msg): - """ exit testing process as if KeyboardInterrupt was triggered. """ +def exit(msg, returncode=None): + """ + Exit testing process as if KeyboardInterrupt was triggered. + + :param str msg: message to display upon exit. + :param int returncode: return code to be used when exiting pytest. + """ __tracebackhide__ = True - raise Exit(msg) + raise Exit(msg, returncode) exit.Exception = Exit diff --git a/testing/test_runner.py b/testing/test_runner.py index b9538cfad19..a3fffe81a3d 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -570,6 +570,18 @@ def pytest_configure(config): result.stderr.fnmatch_lines(["Exit: oh noes"]) +def test_pytest_exit_returncode(testdir): + testdir.makepyfile( + """ + import pytest + def test_foo(): + pytest.exit("some exit msg", 99) + """ + ) + result = testdir.runpytest() + assert result.ret == 99 + + def test_pytest_fail_notrace_runtest(testdir): """Test pytest.fail(..., pytrace=False) does not show tracebacks during test run.""" testdir.makepyfile(