Skip to content

Commit e56af36

Browse files
committed
Fix regression: pytest.exit inside an ExceptionGroup now stops the session as expected.
1 parent a6ae78b commit e56af36

File tree

4 files changed

+52
-3
lines changed

4 files changed

+52
-3
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ Olga Matoula
342342
Oliver Bestwalter
343343
Omar Kohl
344344
Omer Hadari
345+
Omri Golan
345346
Ondřej Súkup
346347
Oscar Benjamin
347348
Parth Patel

changelog/13650.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue where internal exceptions like those raised from :func:`pytest.exit` would not be correctly handled during test teardown.

src/_pytest/runner.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,26 @@ def from_call(
342342
instant = timing.Instant()
343343
try:
344344
result: TResult | None = func()
345-
except BaseException:
345+
except BaseException as caught:
346346
excinfo = ExceptionInfo.from_current()
347-
if reraise is not None and isinstance(excinfo.value, reraise):
348-
raise
347+
val = excinfo.value
348+
349+
if reraise is not None:
350+
reraise_types = (
351+
(reraise,) if not isinstance(reraise, tuple) else reraise
352+
)
353+
354+
# ExceptionGroup-aware path: check if any of the direct children
355+
# is an instance of the `reraise` parameter, and reraise the exception
356+
# accordingly (#13650).
357+
if isinstance(val, BaseExceptionGroup):
358+
for child in val.exceptions:
359+
if isinstance(child, reraise_types):
360+
raise child from caught
361+
# Not an exception group, check if we need to reraise it.
362+
elif isinstance(val, reraise_types):
363+
raise
364+
349365
result = None
350366
duration = instant.elapsed()
351367
return cls(

testing/test_runner.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,3 +1264,34 @@ def test_bar(): pass
12641264
)
12651265
result = pytester.runpytest("--stepwise")
12661266
result.assert_outcomes(failed=1, errors=1)
1267+
1268+
1269+
def test_exit_in_teardown_exception_group_stops_session(pytester: Pytester) -> None:
1270+
pytester.makepyfile(
1271+
test_it="""
1272+
import pytest
1273+
@pytest.fixture
1274+
def failing_teardown():
1275+
yield
1276+
raise IOError("Exception in teardown")
1277+
@pytest.fixture
1278+
def exit_session():
1279+
yield
1280+
pytest.exit("Forced exit")
1281+
def test_1(): return
1282+
@pytest.mark.usefixtures(
1283+
"failing_teardown",
1284+
"exit_session"
1285+
)
1286+
def test_failure(): return
1287+
def test_3(): return
1288+
"""
1289+
)
1290+
result = pytester.runpytest()
1291+
result.assert_outcomes(passed=2)
1292+
result.stdout.fnmatch_lines(
1293+
[
1294+
"!* _pytest.outcomes.Exit: Forced exit !*",
1295+
"=* 2 passed in * =*",
1296+
]
1297+
)

0 commit comments

Comments
 (0)