Skip to content

Commit 2bb9e56

Browse files
gpsheadclaude
andcommitted
gh-87512: Fix subprocess communicate() timeout on Windows for large stdin
On Windows, Popen._communicate() previously wrote to stdin synchronously, which could block indefinitely if the subprocess didn't consume input quickly and the pipe buffer filled up. The timeout parameter was only checked when joining the reader threads, not during the stdin write. This change moves the stdin writing to a background thread (similar to how stdout/stderr are read in threads), allowing the timeout to be properly enforced. If timeout expires, TimeoutExpired is raised promptly and the writer thread continues in the background. Subsequent calls to communicate() will join the existing writer thread. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9b7fe29 commit 2bb9e56

File tree

1 file changed

+21
-2
lines changed

1 file changed

+21
-2
lines changed

Lib/subprocess.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,10 @@ def _readerthread(self, fh, buffer):
16131613
fh.close()
16141614

16151615

1616+
def _writerthread(self, input):
1617+
self._stdin_write(input)
1618+
1619+
16161620
def _communicate(self, input, endtime, orig_timeout):
16171621
# Start reader threads feeding into a list hanging off of this
16181622
# object, unless they've already been started.
@@ -1631,8 +1635,23 @@ def _communicate(self, input, endtime, orig_timeout):
16311635
self.stderr_thread.daemon = True
16321636
self.stderr_thread.start()
16331637

1634-
if self.stdin:
1635-
self._stdin_write(input)
1638+
# Start writer thread to send input to stdin, unless already
1639+
# started. The thread writes input and closes stdin when done,
1640+
# or continues in the background on timeout.
1641+
if self.stdin and not hasattr(self, "_stdin_thread"):
1642+
self._stdin_thread = \
1643+
threading.Thread(target=self._writerthread,
1644+
args=(input,))
1645+
self._stdin_thread.daemon = True
1646+
self._stdin_thread.start()
1647+
1648+
# Wait for the writer thread, or time out. If we time out, the
1649+
# thread remains writing and the fd left open in case the user
1650+
# calls communicate again.
1651+
if hasattr(self, "_stdin_thread"):
1652+
self._stdin_thread.join(self._remaining_time(endtime))
1653+
if self._stdin_thread.is_alive():
1654+
raise TimeoutExpired(self.args, orig_timeout)
16361655

16371656
# Wait for the reader threads, or time out. If we time out, the
16381657
# threads remain reading and the fds left open in case the user

0 commit comments

Comments
 (0)