diff --git a/CHANGELOG b/CHANGELOG index dd35ee914..97cf30dd1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +Changes between 2.2.4 and 2.2.5.dev +----------------------------------- + +- fix issue128: show captured output when capsys/capfd are used + Changes between 2.2.3 and 2.2.4 ----------------------------------- diff --git a/_pytest/__init__.py b/_pytest/__init__.py index 1febee1e1..8f181ddf4 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.4' +__version__ = '2.2.5.dev1' diff --git a/_pytest/capture.py b/_pytest/capture.py index dad9c73fe..221218266 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -121,20 +121,21 @@ class CaptureManager: def activate_funcargs(self, pyfuncitem): if not hasattr(pyfuncitem, 'funcargs'): return - assert not hasattr(self, '_capturing_funcargs') - self._capturing_funcargs = capturing_funcargs = [] + assert not hasattr(self, '_capturing_funcarg') + capargs = [] for name, capfuncarg in pyfuncitem.funcargs.items(): if name in ('capsys', 'capfd'): - capturing_funcargs.append(capfuncarg) - capfuncarg._start() + capargs.append(capfuncarg) + if capargs: + self._capturing_funcarg = capargs[0] + self._capturing_funcarg._start() def deactivate_funcargs(self): - capturing_funcargs = getattr(self, '_capturing_funcargs', None) - if capturing_funcargs is not None: - while capturing_funcargs: - capfuncarg = capturing_funcargs.pop() - capfuncarg._finalize() - del self._capturing_funcargs + capturing_funcarg = getattr(self, '_capturing_funcarg', None) + if capturing_funcarg: + outerr = capturing_funcarg._finalize() + del self._capturing_funcarg + return outerr def pytest_make_collect_report(self, __multicall__, collector): method = self._getmethod(collector.config, collector.fspath) @@ -169,9 +170,12 @@ class CaptureManager: @pytest.mark.tryfirst def pytest_runtest_makereport(self, __multicall__, item, call): - self.deactivate_funcargs() + funcarg_outerr = self.deactivate_funcargs() rep = __multicall__.execute() outerr = self.suspendcapture(item) + if funcarg_outerr is not None: + outerr = (outerr[0] + funcarg_outerr[0], + outerr[1] + funcarg_outerr[1]) if not rep.passed: addouterr(rep, outerr) if not rep.passed or rep.when == "teardown": @@ -179,11 +183,15 @@ class CaptureManager: item.outerr = outerr return rep +error_capsysfderror = "cannot use capsys and capfd at the same time" + def pytest_funcarg__capsys(request): """enables capturing of writes to sys.stdout/sys.stderr and makes captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ + if "capfd" in request._funcargs: + raise request.LookupError(error_capsysfderror) return CaptureFuncarg(py.io.StdCapture) def pytest_funcarg__capfd(request): @@ -191,8 +199,10 @@ def pytest_funcarg__capfd(request): captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. """ + if "capsys" in request._funcargs: + raise request.LookupError(error_capsysfderror) if not hasattr(os, 'dup'): - py.test.skip("capfd funcarg needs os.dup") + pytest.skip("capfd funcarg needs os.dup") return CaptureFuncarg(py.io.StdCaptureFD) class CaptureFuncarg: @@ -204,8 +214,9 @@ class CaptureFuncarg: def _finalize(self): if hasattr(self, 'capture'): - self.capture.reset() + outerr = self.capture.reset() del self.capture + return outerr def readouterr(self): return self.capture.readouterr() diff --git a/setup.py b/setup.py index aa96b4e84..22dac2943 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def main(): name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.2.4', + version='2.2.5.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/test_capture.py b/testing/test_capture.py index 2cfd013a9..b23846425 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -373,6 +373,33 @@ class TestCaptureFuncarg: """) reprec.assertoutcome(passed=1) + def test_capsyscapfd(self, testdir): + p = testdir.makepyfile(""" + def test_one(capsys, capfd): + pass + def test_two(capfd, capsys): + pass + """) + result = testdir.runpytest(p) + result.stdout.fnmatch_lines([ + "*ERROR*setup*test_one*", + "*capsys*capfd*same*time*", + "*ERROR*setup*test_two*", + "*capsys*capfd*same*time*", + "*2 error*"]) + + @pytest.mark.parametrize("method", ["sys", "fd"]) + def test_capture_is_represented_on_failure_issue128(self, testdir, method): + p = testdir.makepyfile(""" + def test_hello(cap%s): + print ("xxx42xxx") + assert 0 + """ % method) + result = testdir.runpytest(p) + result.stdout.fnmatch_lines([ + "xxx42xxx", + ]) + @needsosdup def test_stdfd_functional(self, testdir): reprec = testdir.inline_runsource("""