@@ -1034,6 +1034,62 @@ def test_communicate_timeout_large_output(self):
10341034 (stdout, _) =p.communicate()
10351035self.assertEqual(len(stdout), 4*64*1024)
103610361037+deftest_communicate_timeout_large_input(self):
1038+# Test that timeout is enforced when writing large input to a
1039+# slow-to-read subprocess, and that partial input is preserved
1040+# for continuation after timeout (gh-141473).
1041+#
1042+# This is a regression test for Windows matching POSIX behavior.
1043+# On POSIX, select() is used to multiplex I/O with timeout checking.
1044+# On Windows, stdin writing must also honor the timeout rather than
1045+# blocking indefinitely when the pipe buffer fills.
1046+1047+# Input larger than typical pipe buffer (4-64KB on Windows)
1048+input_data=b"x"* (128*1024)
1049+1050+p=subprocess.Popen(
1051+ [sys.executable, "-c",
1052+"import sys, time; "
1053+"time.sleep(30); "# Don't read stdin for a long time
1054+"sys.stdout.buffer.write(sys.stdin.buffer.read())"],
1055+stdin=subprocess.PIPE,
1056+stdout=subprocess.PIPE,
1057+stderr=subprocess.PIPE)
1058+1059+try:
1060+timeout=0.2
1061+start=time.monotonic()
1062+try:
1063+p.communicate(input_data, timeout=timeout)
1064+# If we get here without TimeoutExpired, the timeout was ignored
1065+elapsed=time.monotonic() -start
1066+self.fail(
1067+f"TimeoutExpired not raised. communicate() completed in "
1068+f"{elapsed:.2f}s, but subprocess sleeps for 30s. "
1069+"Stdin writing blocked without enforcing timeout.")
1070+exceptsubprocess.TimeoutExpired:
1071+elapsed=time.monotonic() -start
1072+1073+# Timeout should occur close to the specified timeout value,
1074+# not after waiting for the subprocess to finish sleeping.
1075+# Allow generous margin for slow CI, but must be well under
1076+# the subprocess sleep time.
1077+self.assertLess(elapsed, 5.0,
1078+f"TimeoutExpired raised after {elapsed:.2f}s; expected ~{timeout}s. "
1079+"Stdin writing blocked without checking timeout.")
1080+1081+# After timeout, continue communication. The remaining input
1082+# should be sent and we should receive all data back.
1083+stdout, stderr=p.communicate()
1084+1085+# Verify all input was eventually received by the subprocess
1086+self.assertEqual(len(stdout), len(input_data),
1087+f"Expected {len(input_data)} bytes output but got {len(stdout)}")
1088+self.assertEqual(stdout, input_data)
1089+finally:
1090+p.kill()
1091+p.wait()
1092+10371093# Test for the fd leak reported in http://bugs.python.org/issue2791.
10381094deftest_communicate_pipe_fd_leak(self):
10391095forstdin_pipein (False, True):