From fc4aa27caebe6bcdcd3cefd7771df8fb2ef5e6b5 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 2 Nov 2018 19:50:29 +0100 Subject: [PATCH] Derive outcomes.exit.Exception from SystemExit instead of KeyboardInterrupt This is required for properly getting out of pdb, where KeyboardInterrupt is caught in py36 at least. Ref: https://github.com/pytest-dev/pytest/issues/1865#issuecomment-242599949 --- changelog/4292.feature.rst | 1 + src/_pytest/main.py | 2 +- src/_pytest/outcomes.py | 6 +++--- src/_pytest/runner.py | 8 +++++--- testing/test_runner.py | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 changelog/4292.feature.rst diff --git a/changelog/4292.feature.rst b/changelog/4292.feature.rst new file mode 100644 index 000000000..27d113ba0 --- /dev/null +++ b/changelog/4292.feature.rst @@ -0,0 +1 @@ +``pytest.outcomes.Exit`` is derived from ``SystemExit`` instead of ``KeyboardInterrupt``. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 08490f03a..d0d826bb6 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -205,7 +205,7 @@ def wrap_session(config, doit): raise except Failed: session.exitstatus = EXIT_TESTSFAILED - except KeyboardInterrupt: + except (KeyboardInterrupt, exit.Exception): excinfo = _pytest._code.ExceptionInfo.from_current() exitstatus = EXIT_INTERRUPTED if initstate <= 2 and isinstance(excinfo.value, exit.Exception): diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index cd08c0d48..714be3088 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -49,13 +49,13 @@ class Failed(OutcomeException): __module__ = "builtins" -class Exit(KeyboardInterrupt): +class Exit(SystemExit): """ raised for immediate program exits (no tracebacks/summaries)""" def __init__(self, msg="unknown reason", returncode=None): self.msg = msg self.returncode = returncode - KeyboardInterrupt.__init__(self, msg) + SystemExit.__init__(self, msg) # exposed helper methods @@ -63,7 +63,7 @@ class Exit(KeyboardInterrupt): def exit(msg, returncode=None): """ - Exit testing process as if KeyboardInterrupt was triggered. + Exit testing process as if SystemExit was triggered. :param str msg: message to display upon exit. :param int returncode: return code to be used when exiting pytest. diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 27f244a80..538e13403 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -15,6 +15,7 @@ from .reports import CollectErrorRepr from .reports import CollectReport from .reports import TestReport from _pytest._code.code import ExceptionInfo +from _pytest.outcomes import Exit from _pytest.outcomes import skip from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME @@ -190,10 +191,11 @@ def check_interactive_exception(call, report): def call_runtest_hook(item, when, **kwds): hookname = "pytest_runtest_" + when ihook = getattr(item.ihook, hookname) + reraise = (Exit,) + if not item.config.getvalue("usepdb"): + reraise += (KeyboardInterrupt,) return CallInfo.from_call( - lambda: ihook(item=item, **kwds), - when=when, - reraise=KeyboardInterrupt if not item.config.getvalue("usepdb") else (), + lambda: ihook(item=item, **kwds), when=when, reraise=reraise ) diff --git a/testing/test_runner.py b/testing/test_runner.py index 916c2ea4a..ae129d06d 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -553,7 +553,7 @@ def test_outcomeexception_passes_except_Exception(): def test_pytest_exit(): with pytest.raises(pytest.exit.Exception) as excinfo: pytest.exit("hello") - assert excinfo.errisinstance(KeyboardInterrupt) + assert excinfo.errisinstance(pytest.exit.Exception) def test_pytest_fail():