-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Open
Labels
type: enhancementnew feature or API change, should be merged into features branchnew feature or API change, should be merged into features branch
Description
Currently if an exception is raised during the setup
or teardown
phase of a fixture, pytest handles that and shows a nice traceback:
import pytest
@pytest.fixture
def my_setup() -> None:
raise ValueError("e1")
def test(my_setup) -> None:
pass
λ pytest bar.py --no-header
======================== test session starts ========================
collected 1 item
bar.py E [100%]
============================== ERRORS ===============================
______________________ ERROR at setup of test _______________________
@pytest.fixture
def my_setup() -> None:
> raise ValueError("e1")
E ValueError: e1
bar.py:6: ValueError
====================== short test summary info ======================
ERROR bar.py::test - ValueError: e1
========================= 1 error in 0.23s ==========================
However if we raise an ExceptionGroup
, there is no special handling and the full traceback is shown:
import pytest
@pytest.fixture
def my_setup() -> None:
raise ExceptionGroup("some errors", [ValueError("e1"), ValueError("e2")])
def test(my_setup) -> None:
pass
λ pytest bar.py --no-header
======================== test session starts ========================
collected 1 item
bar.py E [100%]
============================== ERRORS ===============================
______________________ ERROR at setup of test _______________________
+ Exception Group Traceback (most recent call last):
| File "E:\projects\pytest\src\_pytest\runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| ^^^^^^
| File "E:\projects\pytest\src\_pytest\runner.py", line 241, in <lambda>
| lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
| return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
| raise exception.with_traceback(exception.__traceback__)
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
| teardown.throw(exception) # type: ignore[union-attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\unraisableexception.py", line 85, in pytest_runtest_setup
| yield from unraisable_exception_runtest_hook()
| File "E:\projects\pytest\src\_pytest\unraisableexception.py", line 65, in unraisable_exception_runtest_hook
| yield
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
| teardown.throw(exception) # type: ignore[union-attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\logging.py", line 844, in pytest_runtest_setup
| yield from self._runtest_for(item, "setup")
| File "E:\projects\pytest\src\_pytest\logging.py", line 833, in _runtest_for
| yield
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
| teardown.throw(exception) # type: ignore[union-attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\capture.py", line 873, in pytest_runtest_setup
| return (yield)
| ^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
| teardown.throw(exception) # type: ignore[union-attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\threadexception.py", line 82, in pytest_runtest_setup
| yield from thread_exception_runtest_hook()
| File "E:\projects\pytest\src\_pytest\threadexception.py", line 63, in thread_exception_runtest_hook
| yield
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
| res = hook_impl.function(*args)
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\runner.py", line 159, in pytest_runtest_setup
| item.session._setupstate.setup(item)
| File "E:\projects\pytest\src\_pytest\runner.py", line 515, in setup
| raise exc
| File "E:\projects\pytest\src\_pytest\runner.py", line 512, in setup
| col.setup()
| File "E:\projects\pytest\src\_pytest\python.py", line 1630, in setup
| self._request._fillfixtures()
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 695, in _fillfixtures
| item.funcargs[argname] = self.getfixturevalue(argname)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 552, in getfixturevalue
| fixturedef = self._get_active_fixturedef(argname)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 581, in _get_active_fixturedef
| self._compute_fixture_value(fixturedef)
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 656, in _compute_fixture_value
| fixturedef.execute(request=subrequest)
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 1086, in execute
| result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
| return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
| raise exception.with_traceback(exception.__traceback__)
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
| teardown.throw(exception) # type: ignore[union-attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\setuponly.py", line 36, in pytest_fixture_setup
| return (yield)
| ^^^^^
| File "E:\projects\pytest\.env312\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
| res = hook_impl.function(*args)
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 1135, in pytest_fixture_setup
| result = call_fixture_func(fixturefunc, request, kwargs)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "E:\projects\pytest\src\_pytest\fixtures.py", line 903, in call_fixture_func
| fixture_result = fixturefunc(**kwargs)
| ^^^^^^^^^^^^^^^^^^^^^
| File "e:\projects\pytest\.tmp\bar.py", line 7, in my_setup
| raise ExceptionGroup("some errors", [ValueError("e1"), ValueError("e2")])
| ExceptionGroup: some errors (2 sub-exceptions)
+-+---------------- 1 ----------------
| ValueError: e1
+---------------- 2 ----------------
| ValueError: e2
+------------------------------------
====================== short test summary info ======================
ERROR bar.py::test - ExceptionGroup: some errors (2 sub-exceptions)
========================= 1 error in 0.05s ==========================
I think pytest should be able to also handle ExceptionGroup
and show a nicer traceback.
Noticed this while working on #12250.
StefanBRas, neutrinoceros and patrick-kidger
Metadata
Metadata
Assignees
Labels
type: enhancementnew feature or API change, should be merged into features branchnew feature or API change, should be merged into features branch