Skip to content

Commit 6e850d9

Browse files
committed
fix #13537: Add support for ExceptionGroup with only Skipped exceptions in teardown
1 parent ed58491 commit 6e850d9

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

src/_pytest/reports.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,26 +274,30 @@ def _format_exception_group_all_skipped_longrepr(
274274
excinfo: ExceptionInfo[BaseException],
275275
exceptions: list[Skipped],
276276
) -> tuple[str, int, str]:
277+
r = excinfo._getreprcrash()
278+
assert r is not None, (
279+
"There should always be a traceback entry for skipping a test."
280+
)
277281
if any(getattr(skip, "_use_item_location", False) for skip in exceptions):
278282
path, line = item.reportinfo()[:2]
279283
assert line is not None
280284
loc = (os.fspath(path), line + 1)
281285
default_msg = "skipped"
286+
# longrepr = (*loc, r.message)
282287
else:
283-
r = excinfo._getreprcrash()
284288
assert r is not None
285289
loc = (str(r.path), r.lineno)
286290
default_msg = r.message
287291

288-
# reason(s): order-preserving de-dupe, same fields as single-skip
289-
msgs: list[str] = []
290-
for exception in exceptions:
291-
m = exception.msg or exception.args[0]
292-
if m and m not in msgs:
293-
msgs.append(m)
292+
# reason(s): order-preserving de-dupe, same fields as single-skip
293+
msgs: list[str] = []
294+
for exception in exceptions:
295+
m = exception.msg or exception.args[0]
296+
if m and m not in msgs:
297+
msgs.append(m)
294298

295-
reason = "; ".join(msgs) if msgs else default_msg
296-
longrepr = (*loc, reason)
299+
reason = "; ".join(msgs) if msgs else default_msg
300+
longrepr = (*loc, reason)
297301
return longrepr
298302

299303

testing/test_reports.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,16 +450,56 @@ def fixA():
450450
@pytest.fixture
451451
def fixB():
452452
yield
453-
pytest.skip(reason="A")
453+
pytest.skip(reason="B")
454454
def test_skip(
455455
fixA,
456456
fixB
457457
):
458458
assert True
459459
"""
460460
)
461-
result = pytester.runpytest("-vv")
461+
result = pytester.runpytest("-v")
462462
result.assert_outcomes(passed=1, skipped=1)
463+
out = result.stdout.str()
464+
# Both reasons should appear
465+
assert "A" in out and "B" in out
466+
assert "ERROR at teardown" not in out
467+
468+
def test_exception_group_skips_use_item_location(self, pytester: Pytester):
469+
"""
470+
Regression for #13537:
471+
If any skip inside an ExceptionGroup has _use_item_location=True,
472+
the report location should point to the test item, not the fixture teardown.
473+
"""
474+
pytester.makepyfile(
475+
test_it="""
476+
import pytest
477+
478+
@pytest.fixture
479+
def fix_item_loc():
480+
yield
481+
# Create skip.Exception and set flag to use item location
482+
exc = pytest.skip.Exception("A")
483+
exc._use_item_location = True
484+
raise exc
485+
486+
@pytest.fixture
487+
def fix_normal():
488+
yield
489+
raise pytest.skip.Exception("B")
490+
491+
def test_both(fix_item_loc, fix_normal):
492+
assert True
493+
"""
494+
)
495+
result = pytester.runpytest("-v")
496+
result.assert_outcomes(passed=1, skipped=1)
497+
498+
out = result.stdout.str()
499+
# Both reasons should appear
500+
assert "A" in out and "B" in out
501+
# Crucially, the skip should be attributed to the test item, not teardown
502+
assert "test_both" in out
463503

464504

465505
class TestHooks:

0 commit comments

Comments
 (0)