diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 6d98676be5f..bb7e7fde7ab 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -516,10 +516,32 @@ def _assert_state(self, op: str, states: tuple[str, ...]) -> None: ) ) + def _sync_win32_stdhandle(self, targetfd: int) -> None: + if sys.platform != "win32": + return + + import ctypes + import msvcrt + + fd_to_std = { + 0: -10, # STD_INPUT_HANDLE + 1: -11, # STD_OUTPUT_HANDLE + 2: -12, # STD_ERROR_HANDLE + } + + std_id = fd_to_std.get(targetfd) + if std_id is None: + return + + handle = msvcrt.get_osfhandle(targetfd) + ctypes.windll.kernel32.SetStdHandle(std_id, handle) + def start(self) -> None: """Start capturing on targetfd using memorized tmpfile.""" self._assert_state("start", ("initialized",)) os.dup2(self.tmpfile.fileno(), self.targetfd) + + self._sync_win32_stdhandle(self.targetfd) self.syscapture.start() self._state = "started" @@ -530,6 +552,7 @@ def done(self) -> None: if self._state == "done": return os.dup2(self.targetfd_save, self.targetfd) + self._sync_win32_stdhandle(self.targetfd) os.close(self.targetfd_save) if self.targetfd_invalid is not None: if self.targetfd_invalid != self.targetfd: @@ -545,6 +568,7 @@ def suspend(self) -> None: return self.syscapture.suspend() os.dup2(self.targetfd_save, self.targetfd) + self._sync_win32_stdhandle(self.targetfd) self._state = "suspended" def resume(self) -> None: @@ -553,6 +577,7 @@ def resume(self) -> None: return self.syscapture.resume() os.dup2(self.tmpfile.fileno(), self.targetfd) + self._sync_win32_stdhandle(self.targetfd) self._state = "started"