-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Open
Labels
needs backportapplied to PRs, indicates that it should be ported to the current bug-fix branchapplied to PRs, indicates that it should be ported to the current bug-fix branchtype: bugproblem that needs to be addressedproblem that needs to be addressed
Description
Hi
I have a situation where I have two teardown fixtures:
- one raises exception
- second performs
pytest.exit()
- I use it to stop testing session when only specific test fails.
Simplified example:
import pytest
@pytest.fixture
def failing_teardown():
yield
raise IOError("Exception in teardown")
@pytest.fixture
def exit_session():
yield
pytest.exit("Forced exit")
def test_1(): return
@pytest.mark.usefixtures("failing_teardown", "exit_session")
def test_failure(): return
def test_3(): return
My expectation is that:
test_1
passestest_2
call passes, teardown reports error (due to exception infailing_teardown
) and test session finishes due topytest.exit()
inexit_session
fixturetest_3
is not executed
But the exception from _pytest.outcomes.Exit is only logged inside ExceptionGroup and exit is not performed:
$ pytest
================================= test session starts ==================================
platform linux -- Python 3.13.3, pytest-8.4.1, pluggy-1.6.0
rootdir: ./pytest-exit-fail
plugins: ordering-0.6
collected 3 items
test_dummy.py ..E. [100%]
======================================== ERRORS ========================================
__________________________ ERROR at teardown of test_failure ___________________________
+ Exception Group Traceback (most recent call last):
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 344, in from_call
| result: TResult | None = func()
| ~~~~^^
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 246, in <lambda>
| lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
| ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/pluggy/_hooks.py", line 512, in __call__
| return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
| ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/pluggy/_manager.py", line 120, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/pluggy/_callers.py", line 167, in _multicall
| raise exception
| File ".venv/lib/python3.13/site-packages/pluggy/_callers.py", line 139, in _multicall
| teardown.throw(exception)
| ~~~~~~~~~~~~~~^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/_pytest/logging.py", line 858, in pytest_runtest_teardown
| yield
| File ".venv/lib/python3.13/site-packages/pluggy/_callers.py", line 139, in _multicall
| teardown.throw(exception)
| ~~~~~~~~~~~~~~^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/_pytest/capture.py", line 905, in pytest_runtest_teardown
| return (yield)
| ^^^^^
| File ".venv/lib/python3.13/site-packages/pluggy/_callers.py", line 121, in _multicall
| res = hook_impl.function(*args)
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 193, in pytest_runtest_teardown
| item.session._setupstate.teardown_exact(nextitem)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 557, in teardown_exact
| raise exceptions[0]
| ExceptionGroup: errors while tearing down <Function test_failure> (2 sub-exceptions)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 546, in teardown_exact
| fin()
| ~~~^^
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 1068, in finish
| raise exceptions[0]
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 1057, in finish
| fin()
| ~~~^^
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 938, in _teardown_yield_fixture
| next(it)
| ~~~~^^^^
| File "./test_dummy.py", line 7, in failing_teardown
| raise IOError("Exception in teardown")
| OSError: Exception in teardown
+---------------- 2 ----------------
| Traceback (most recent call last):
| File ".venv/lib/python3.13/site-packages/_pytest/runner.py", line 546, in teardown_exact
| fin()
| ~~~^^
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 1068, in finish
| raise exceptions[0]
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 1057, in finish
| fin()
| ~~~^^
| File ".venv/lib/python3.13/site-packages/_pytest/fixtures.py", line 938, in _teardown_yield_fixture
| next(it)
| ~~~~^^^^
| File "./test_dummy.py", line 13, in exit_session
| pytest.exit("Forced exit")
| ~~~~~~~~~~~^^^^^^^^^^^^^^^
| File ".venv/lib/python3.13/site-packages/_pytest/outcomes.py", line 122, in exit
| raise Exit(reason, returncode)
| _pytest.outcomes.Exit: Forced exit
+------------------------------------
=============================== short test summary info ================================
ERROR test_dummy.py::test_failure - ExceptionGroup: errors while tearing down <Function test_failure> (2 sub-exceptions)
============================== 3 passed, 1 error in 0.01s ==============================
If I remove failing_teardown
fixture, pytest.exit()
works:
import pytest
@pytest.fixture
def exit_session():
yield
pytest.exit("Forced exit")
def test_1(): return
@pytest.mark.usefixtures("exit_session")
def test_failure(): return
def test_3(): return
log:
$ pytest
================================= test session starts ==================================
platform linux -- Python 3.13.3, pytest-8.4.1, pluggy-1.6.0
rootdir: ./pytest-exit-fail
plugins: ordering-0.6
collected 3 items
test_dummy.py ..
!!!!!!!!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Forced exit !!!!!!!!!!!!!!!!!!!!!!!!!!
================================== 2 passed in 0.15s ===================================
Environment:
- platform: linux
- Python 3.13.3
- pytest-8.4.1
- pluggy-1.6.0
Metadata
Metadata
Assignees
Labels
needs backportapplied to PRs, indicates that it should be ported to the current bug-fix branchapplied to PRs, indicates that it should be ported to the current bug-fix branchtype: bugproblem that needs to be addressedproblem that needs to be addressed