[3.13] gh-141473: Fix subprocess.Popen.communicate to send input to stdin upon a subsequent post-timeout call (GH-141477) (#142060)

* gh-141473: Fix subprocess.Popen.communicate to send input to stdin upon a subsequent post-timeout call (GH-141477)

* gh-141473: Fix subprocess.Popen.communicate to send input to stdin
* Docs: Clarify that `input` is one time only on `communicate()`
* NEWS entry
* Add a regression test.

---------
(cherry picked from commit 526d7a8bb4)

Co-authored-by: Artur Jamro <artur.jamro@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>

* no assertStartsWith

---------

Co-authored-by: Artur Jamro <artur.jamro@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
Miss Islington (bot) 2025-11-29 07:53:00 +01:00 committed by GitHub
parent 704bb69bd8
commit 2f3024f066
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 2 deletions

View file

@ -1684,6 +1684,40 @@ def test_wait_negative_timeout(self):
self.assertEqual(proc.wait(), 0)
def test_post_timeout_communicate_sends_input(self):
"""GH-141473 regression test; the stdin pipe must close"""
with subprocess.Popen(
[sys.executable, "-uc", """\
import sys
while c := sys.stdin.read(512):
sys.stdout.write(c)
print()
"""],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
) as proc:
try:
data = f"spam{'#'*4096}beans"
proc.communicate(
input=data,
timeout=0,
)
except subprocess.TimeoutExpired as exc:
pass
# Prior to the bugfix, this would hang as the stdin
# pipe to the child had not been closed.
try:
stdout, stderr = proc.communicate(timeout=15)
except subprocess.TimeoutExpired as exc:
self.fail("communicate() hung waiting on child process that should have seen its stdin pipe close and exit")
self.assertEqual(
proc.returncode, 0,
msg=f"STDERR:\n{stderr}\nSTDOUT:\n{stdout}")
self.assertTrue(stdout.startswith("spam"), msg=stdout)
self.assertIn("beans", stdout)
class RunFuncTestCase(BaseTestCase):
def run_python(self, code, **kwargs):