From 99d162e44a0d40675b855dbcde9734b29032f8aa Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 2 Feb 2020 22:23:41 +0100 Subject: [PATCH] Handle `Exit` exception in `pytest_sessionfinish` Similar to a7268aa (https://github.com/pytest-dev/pytest/pull/6258). --- changelog/6660.bugfix.rst | 1 + src/_pytest/main.py | 11 ++++++++--- testing/test_main.py | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 changelog/6660.bugfix.rst diff --git a/changelog/6660.bugfix.rst b/changelog/6660.bugfix.rst new file mode 100644 index 000000000..bcc2e1d94 --- /dev/null +++ b/changelog/6660.bugfix.rst @@ -0,0 +1 @@ +:func:`pytest.exit() <_pytest.outcomes.exit>` is handled when emitted from the :func:`pytest_sessionfinish <_pytest.hookspec.pytest_sessionfinish>` hook. This includes quitting from a debugger. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 8ef06db38..59c3c6714 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -244,9 +244,14 @@ def wrap_session( excinfo = None # type: ignore session.startdir.chdir() if initstate >= 2: - config.hook.pytest_sessionfinish( - session=session, exitstatus=session.exitstatus - ) + try: + config.hook.pytest_sessionfinish( + session=session, exitstatus=session.exitstatus + ) + except Exit as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc)) config._ensure_unconfigure() return session.exitstatus diff --git a/testing/test_main.py b/testing/test_main.py index b47791b29..49e3decd0 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -1,5 +1,8 @@ +from typing import Optional + import pytest from _pytest.main import ExitCode +from _pytest.pytester import Testdir @pytest.mark.parametrize( @@ -50,3 +53,25 @@ def test_wrap_session_notify_exception(ret_exc, testdir): assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"] else: assert result.stderr.lines == ["Exit: exiting after {}...".format(exc.__name__)] + + +@pytest.mark.parametrize("returncode", (None, 42)) +def test_wrap_session_exit_sessionfinish( + returncode: Optional[int], testdir: Testdir +) -> None: + testdir.makeconftest( + """ + import pytest + def pytest_sessionfinish(): + pytest.exit(msg="exit_pytest_sessionfinish", returncode={returncode}) + """.format( + returncode=returncode + ) + ) + result = testdir.runpytest() + if returncode: + assert result.ret == returncode + else: + assert result.ret == ExitCode.NO_TESTS_COLLECTED + assert result.stdout.lines[-1] == "collected 0 items" + assert result.stderr.lines == ["Exit: exit_pytest_sessionfinish"]