diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index 5799b00e6..64d21b9f6 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -742,7 +742,10 @@ class FixtureDef: except: exceptions.append(sys.exc_info()) if exceptions: - py.builtin._reraise(*exceptions[0]) + e = exceptions[0] + del exceptions # ensure we don't keep all frames alive because of the traceback + py.builtin._reraise(*e) + finally: ihook = self._fixturemanager.session.ihook ihook.pytest_fixture_post_finalizer(fixturedef=self) diff --git a/changelog/2440.bugfix b/changelog/2440.bugfix index 6f64d1dee..7f1f7d504 100644 --- a/changelog/2440.bugfix +++ b/changelog/2440.bugfix @@ -1 +1 @@ -Exceptions in a SubRequest's finish() block are suppressed until all finalizers are called, with the initial exception reraised. \ No newline at end of file +Exceptions raised during teardown by finalizers are now suppressed until all finalizers are called, with the initial exception reraised. diff --git a/testing/python/fixture.py b/testing/python/fixture.py index ca2e078bd..1e58a5550 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -658,11 +658,15 @@ class TestRequestBasic(object): ]) def test_request_subrequest_addfinalizer_exceptions(self, testdir): + """ + Ensure exceptions raised during teardown by a finalizer are suppressed + until all finalizers are called, re-raising the first exception (#2440) + """ testdir.makepyfile(""" import pytest l = [] - def _excepts(): - raise Exception('Error') + def _excepts(where): + raise Exception('Error in %s fixture' % where) @pytest.fixture def subrequest(request): return request @@ -670,18 +674,21 @@ class TestRequestBasic(object): def something(subrequest): subrequest.addfinalizer(lambda: l.append(1)) subrequest.addfinalizer(lambda: l.append(2)) - subrequest.addfinalizer(_excepts) + subrequest.addfinalizer(lambda: _excepts('something')) @pytest.fixture def excepts(subrequest): - subrequest.addfinalizer(_excepts) + subrequest.addfinalizer(lambda: _excepts('excepts')) subrequest.addfinalizer(lambda: l.append(3)) def test_first(something, excepts): pass def test_second(): assert l == [3, 2, 1] """) - reprec = testdir.inline_run() - reprec.assertoutcome(passed=2, failed=1) + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + '*Exception: Error in excepts fixture', + '* 2 passed, 1 error in *', + ]) def test_request_getmodulepath(self, testdir): modcol = testdir.getmodulecol("def test_somefunc(): pass")