diff --git a/CHANGELOG b/CHANGELOG index c5bdf6a89..09de8bba0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,9 @@ Changes between 1.X and 1.1.1 - new "pytestconfig" funcarg allows access to test config object +- robustify capturing to survive if custom pytest_runtest_setup + code failed and prevented the capturing setup code from running. + - make py.test.* helpers provided by default plugins visible early - works transparently both for pydoc and for interactive sessions which will regularly see e.g. py.test.mark and py.test.importorskip. diff --git a/py/plugin/pytest_capture.py b/py/plugin/pytest_capture.py index e511ecfbc..78cc57275 100644 --- a/py/plugin/pytest_capture.py +++ b/py/plugin/pytest_capture.py @@ -161,16 +161,20 @@ class CaptureManager: cap.resume() self._capturing = method - def suspendcapture(self): + def suspendcapture(self, item=None): self.deactivate_funcargs() - method = self._capturing - if method != "no": - cap = self._method2capture[method] - outerr = cap.suspend() - else: - outerr = "", "" - del self._capturing - return outerr + if hasattr(self, '_capturing'): + method = self._capturing + if method != "no": + cap = self._method2capture[method] + outerr = cap.suspend() + else: + outerr = "", "" + del self._capturing + if item: + outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1]) + return outerr + return "", "" def activate_funcargs(self, pyfuncitem): if not hasattr(pyfuncitem, 'funcargs'): @@ -210,9 +214,6 @@ class CaptureManager: def pytest_runtest_teardown(self, item): self.resumecapture_item(item) - def pytest_runtest_teardown(self, item): - self.resumecapture_item(item) - def pytest__teardown_final(self, __multicall__, session): method = self._getmethod(session.config, None) self.resumecapture(method) @@ -231,8 +232,7 @@ class CaptureManager: def pytest_runtest_makereport(self, __multicall__, item, call): self.deactivate_funcargs() rep = __multicall__.execute() - outerr = self.suspendcapture() - outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1]) + outerr = self.suspendcapture(item) if not rep.passed: addouterr(rep, outerr) if not rep.passed or rep.when == "teardown": diff --git a/testing/plugin/test_pytest_capture.py b/testing/plugin/test_pytest_capture.py index 2107a45d5..1ba7c2b72 100644 --- a/testing/plugin/test_pytest_capture.py +++ b/testing/plugin/test_pytest_capture.py @@ -379,3 +379,15 @@ class TestCaptureFuncarg: ]) assert result.ret == 2 +def test_setup_failure_does_not_kill_capturing(testdir): + sub1 = testdir.mkpydir("sub1") + sub1.join("conftest.py").write(py.code.Source(""" + def pytest_runtest_setup(item): + raise ValueError(42) + """)) + sub1.join("test_mod.py").write("def test_func1(): pass") + result = testdir.runpytest(testdir.tmpdir, '--traceconfig') + result.stdout.fnmatch_lines([ + "*ValueError(42)*", + "*1 error*" + ])