File tree 3 files changed +79
-1
lines changed
3 files changed +79
-1
lines changed Original file line number Diff line number Diff line change
1
+ If a test raises an ``ExceptionGroup `` (or nested ``ExceptionGroup ``\ s) with only
2
+ a single 'leaf' exception from ``pytest.xfail() `` or ``pytest.skip() ``\ , we now
3
+ unwrap it to have the desired effect on Pytest. ``ExceptionGroup ``\ s with two or
4
+ more leaf exceptions, even of the same type, are not changed and will be treated
5
+ as ordinary test failures.
6
+
7
+ See `pytest-dev/pytest#9680 <https://github.com/pytest-dev/pytest/issues/9680 >`__
8
+ for design discussion. This feature is particularly useful if you've enabled
9
+ `the new strict_exception_groups=True option
10
+ <https://trio.readthedocs.io/en/stable/reference-core.html#strict-versus-loose-exceptiongroup-semantics> `__.
Original file line number Diff line number Diff line change @@ -74,3 +74,52 @@ def test_invalid():
74
74
result = testdir .runpytest ()
75
75
76
76
result .assert_outcomes (errors = 1 )
77
+
78
+
79
+ def test_skip_and_xfail (testdir ):
80
+
81
+ testdir .makepyfile (
82
+ """
83
+ import functools
84
+ import pytest
85
+ import trio
86
+
87
+ trio.run = functools.partial(trio.run, strict_exception_groups=True)
88
+
89
+ @pytest.mark.trio
90
+ async def test_xfail():
91
+ pytest.xfail()
92
+
93
+ @pytest.mark.trio
94
+ async def test_skip():
95
+ pytest.skip()
96
+
97
+ async def callback(fn):
98
+ fn()
99
+
100
+ async def fail():
101
+ raise RuntimeError
102
+
103
+ @pytest.mark.trio
104
+ async def test_xfail_and_fail():
105
+ async with trio.open_nursery() as nursery:
106
+ nursery.start_soon(callback, pytest.xfail)
107
+ nursery.start_soon(fail)
108
+
109
+ @pytest.mark.trio
110
+ async def test_skip_and_fail():
111
+ async with trio.open_nursery() as nursery:
112
+ nursery.start_soon(callback, pytest.skip)
113
+ nursery.start_soon(fail)
114
+
115
+ @pytest.mark.trio
116
+ async def test_xfail_and_skip():
117
+ async with trio.open_nursery() as nursery:
118
+ nursery.start_soon(callback, pytest.skip)
119
+ nursery.start_soon(callback, pytest.xfail)
120
+ """
121
+ )
122
+
123
+ result = testdir .runpytest ("-s" )
124
+
125
+ result .assert_outcomes (skipped = 1 , xfailed = 1 , failed = 3 )
Original file line number Diff line number Diff line change 10
10
import trio
11
11
from trio .abc import Clock , Instrument
12
12
from trio .testing import MockClock
13
+ from _pytest .outcomes import Skipped , XFailed
13
14
14
15
if sys .version_info [:2 ] < (3 , 11 ):
15
16
from exceptiongroup import BaseExceptionGroup
@@ -343,7 +344,25 @@ def wrapper(**kwargs):
343
344
f"Expected at most one Clock in kwargs, got { clocks !r} "
344
345
)
345
346
instruments = [i for i in kwargs .values () if isinstance (i , Instrument )]
346
- return run (partial (fn , ** kwargs ), clock = clock , instruments = instruments )
347
+ try :
348
+ return run (partial (fn , ** kwargs ), clock = clock , instruments = instruments )
349
+ except BaseExceptionGroup as eg :
350
+ queue = [eg ]
351
+ leaves = []
352
+ while queue :
353
+ ex = queue .pop ()
354
+ if isinstance (ex , BaseExceptionGroup ):
355
+ queue .extend (ex .exceptions )
356
+ else :
357
+ leaves .append (ex )
358
+ if len (leaves ) == 1 :
359
+ if isinstance (leaves [0 ], XFailed ):
360
+ pytest .xfail ()
361
+ if isinstance (leaves [0 ], Skipped ):
362
+ pytest .skip ()
363
+ # Since our leaf exceptions don't consist of exactly one 'magic'
364
+ # skipped or xfailed exception, re-raise the whole group.
365
+ raise
347
366
348
367
return wrapper
349
368
You can’t perform that action at this time.
0 commit comments