Skip to content

Commit fc4aa27

Browse files
committed
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: #1865 (comment)
1 parent 038f1f9 commit fc4aa27

File tree

5 files changed

+11
-8
lines changed

5 files changed

+11
-8
lines changed

changelog/4292.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``pytest.outcomes.Exit`` is derived from ``SystemExit`` instead of ``KeyboardInterrupt``.

src/_pytest/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def wrap_session(config, doit):
205205
raise
206206
except Failed:
207207
session.exitstatus = EXIT_TESTSFAILED
208-
except KeyboardInterrupt:
208+
except (KeyboardInterrupt, exit.Exception):
209209
excinfo = _pytest._code.ExceptionInfo.from_current()
210210
exitstatus = EXIT_INTERRUPTED
211211
if initstate <= 2 and isinstance(excinfo.value, exit.Exception):

src/_pytest/outcomes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ class Failed(OutcomeException):
4949
__module__ = "builtins"
5050

5151

52-
class Exit(KeyboardInterrupt):
52+
class Exit(SystemExit):
5353
""" raised for immediate program exits (no tracebacks/summaries)"""
5454

5555
def __init__(self, msg="unknown reason", returncode=None):
5656
self.msg = msg
5757
self.returncode = returncode
58-
KeyboardInterrupt.__init__(self, msg)
58+
SystemExit.__init__(self, msg)
5959

6060

6161
# exposed helper methods
6262

6363

6464
def exit(msg, returncode=None):
6565
"""
66-
Exit testing process as if KeyboardInterrupt was triggered.
66+
Exit testing process as if SystemExit was triggered.
6767
6868
:param str msg: message to display upon exit.
6969
:param int returncode: return code to be used when exiting pytest.

src/_pytest/runner.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .reports import CollectReport
1616
from .reports import TestReport
1717
from _pytest._code.code import ExceptionInfo
18+
from _pytest.outcomes import Exit
1819
from _pytest.outcomes import skip
1920
from _pytest.outcomes import Skipped
2021
from _pytest.outcomes import TEST_OUTCOME
@@ -190,10 +191,11 @@ def check_interactive_exception(call, report):
190191
def call_runtest_hook(item, when, **kwds):
191192
hookname = "pytest_runtest_" + when
192193
ihook = getattr(item.ihook, hookname)
194+
reraise = (Exit,)
195+
if not item.config.getvalue("usepdb"):
196+
reraise += (KeyboardInterrupt,)
193197
return CallInfo.from_call(
194-
lambda: ihook(item=item, **kwds),
195-
when=when,
196-
reraise=KeyboardInterrupt if not item.config.getvalue("usepdb") else (),
198+
lambda: ihook(item=item, **kwds), when=when, reraise=reraise
197199
)
198200

199201

testing/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ def test_outcomeexception_passes_except_Exception():
553553
def test_pytest_exit():
554554
with pytest.raises(pytest.exit.Exception) as excinfo:
555555
pytest.exit("hello")
556-
assert excinfo.errisinstance(KeyboardInterrupt)
556+
assert excinfo.errisinstance(pytest.exit.Exception)
557557

558558

559559
def test_pytest_fail():

0 commit comments

Comments
 (0)