Skip to content

Commit 336cf3e

Browse files
authored
Merge pull request #2496 from rmfitzpatrick/pytest2440_handle_subrequest_finalizer_exceptions
Handle exceptions in subrequest finalizers
2 parents 0908f40 + 4e4ebbe commit 336cf3e

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

_pytest/fixtures.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,19 @@ def addfinalizer(self, finalizer):
733733
self._finalizer.append(finalizer)
734734

735735
def finish(self):
736+
exceptions = []
736737
try:
737738
while self._finalizer:
738-
func = self._finalizer.pop()
739-
func()
739+
try:
740+
func = self._finalizer.pop()
741+
func()
742+
except:
743+
exceptions.append(sys.exc_info())
744+
if exceptions:
745+
e = exceptions[0]
746+
del exceptions # ensure we don't keep all frames alive because of the traceback
747+
py.builtin._reraise(*e)
748+
740749
finally:
741750
ihook = self._fixturemanager.session.ihook
742751
ihook.pytest_fixture_post_finalizer(fixturedef=self)

changelog/2440.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Exceptions raised during teardown by finalizers are now suppressed until all finalizers are called, with the initial exception reraised.

testing/python/fixture.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,39 @@ def test_second():
657657
"*1 error*" # XXX the whole module collection fails
658658
])
659659

660+
def test_request_subrequest_addfinalizer_exceptions(self, testdir):
661+
"""
662+
Ensure exceptions raised during teardown by a finalizer are suppressed
663+
until all finalizers are called, re-raising the first exception (#2440)
664+
"""
665+
testdir.makepyfile("""
666+
import pytest
667+
l = []
668+
def _excepts(where):
669+
raise Exception('Error in %s fixture' % where)
670+
@pytest.fixture
671+
def subrequest(request):
672+
return request
673+
@pytest.fixture
674+
def something(subrequest):
675+
subrequest.addfinalizer(lambda: l.append(1))
676+
subrequest.addfinalizer(lambda: l.append(2))
677+
subrequest.addfinalizer(lambda: _excepts('something'))
678+
@pytest.fixture
679+
def excepts(subrequest):
680+
subrequest.addfinalizer(lambda: _excepts('excepts'))
681+
subrequest.addfinalizer(lambda: l.append(3))
682+
def test_first(something, excepts):
683+
pass
684+
def test_second():
685+
assert l == [3, 2, 1]
686+
""")
687+
result = testdir.runpytest()
688+
result.stdout.fnmatch_lines([
689+
'*Exception: Error in excepts fixture',
690+
'* 2 passed, 1 error in *',
691+
])
692+
660693
def test_request_getmodulepath(self, testdir):
661694
modcol = testdir.getmodulecol("def test_somefunc(): pass")
662695
item, = testdir.genitems([modcol])

0 commit comments

Comments
 (0)