diff --git a/changelog/13784.bugfix.rst b/changelog/13784.bugfix.rst new file mode 100644 index 00000000000..a2f4cf54327 --- /dev/null +++ b/changelog/13784.bugfix.rst @@ -0,0 +1 @@ +Fixed ``capteesys`` producing doubled output when used with ``--capture=no`` (``-s``). diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 6d98676be5f..f9f8f9e4a28 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -944,7 +944,14 @@ def _start(self) -> None: def close(self) -> None: if self._capture is not None: - out, err = self._capture.pop_outerr_to_orig() + if self._config.get("tee"): + # When tee is enabled, output was already written to the + # original stream in real-time by TeeCaptureIO. Using + # pop_outerr_to_orig() would write it a second time via + # writeorg(), causing doubled output (see #13784). + out, err = self._capture.readouterr() + else: + out, err = self._capture.pop_outerr_to_orig() self._captured_out += out self._captured_err += err self._capture.stop_capturing() diff --git a/testing/test_capture.py b/testing/test_capture.py index 11fd18f08ff..3083d2d7a37 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -478,6 +478,26 @@ def test_one(capteesys): result.stdout.fnmatch_lines(["sTdoUt", "sTdeRr"]) # no tee, just reported assert not result.stderr.lines + def test_capteesys_no_double_output_with_capture_no( + self, pytester: Pytester + ) -> None: + """Capteesys with --capture=no should not produce doubled output (#13784).""" + p = pytester.makepyfile( + """\ + def test_one(capteesys): + print("hello world stdout") + out, err = capteesys.readouterr() + assert out == "hello world stdout\\n" + """ + ) + result = pytester.runpytest(p, "--quiet", "--quiet", "-rN", "-s") + assert result.ret == ExitCode.OK + # "hello world stdout" should appear exactly once, not twice. + count = result.stdout.lines.count("hello world stdout") + assert count == 1, ( + f"Expected 'hello world stdout' once, but found {count} times" + ) + def test_capsyscapfd(self, pytester: Pytester) -> None: p = pytester.makepyfile( """\