summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Marczykowski-Górecki <marmarek@invisiblethingslab.com>2023-04-28 00:30:26 (GMT)
committerGitHub <noreply@github.com>2023-04-28 00:30:26 (GMT)
commit67d140dba72dc2cb661d55878384464de46719e7 (patch)
treef9459ce8747f0910eb8e62c9c175801378e3175f
parent424a785a07049924603228b153f746cfe3a983a2 (diff)
downloadcpython-67d140dba72dc2cb661d55878384464de46719e7.zip
cpython-67d140dba72dc2cb661d55878384464de46719e7.tar.gz
cpython-67d140dba72dc2cb661d55878384464de46719e7.tar.bz2
gh-83925: Make asyncio.subprocess communicate similar to non-asyncio (#18650)
subprocess's communicate(None) closes stdin of the child process, after sending no (extra) data. Make asyncio variant do the same. This fixes issues with processes that waits for EOF on stdin before continuing.
-rw-r--r--Doc/library/asyncio-subprocess.rst9
-rw-r--r--Lib/asyncio/subprocess.py11
-rw-r--r--Lib/test/test_asyncio/test_subprocess.py18
-rw-r--r--Misc/NEWS.d/next/Library/2020-02-25-00-43-22.bpo-39744.hgK689.rst1
4 files changed, 32 insertions, 7 deletions
diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst
index 4274638..b7c83aa 100644
--- a/Doc/library/asyncio-subprocess.rst
+++ b/Doc/library/asyncio-subprocess.rst
@@ -207,8 +207,9 @@ their completion.
Interact with process:
1. send data to *stdin* (if *input* is not ``None``);
- 2. read data from *stdout* and *stderr*, until EOF is reached;
- 3. wait for process to terminate.
+ 2. closes *stdin*;
+ 3. read data from *stdout* and *stderr*, until EOF is reached;
+ 4. wait for process to terminate.
The optional *input* argument is the data (:class:`bytes` object)
that will be sent to the child process.
@@ -229,6 +230,10 @@ their completion.
Note, that the data read is buffered in memory, so do not use
this method if the data size is large or unlimited.
+ .. versionchanged:: 3.12
+
+ *stdin* gets closed when `input=None` too.
+
.. method:: send_signal(signal)
Sends the signal *signal* to the child process.
diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py
index cd10231..50727ca 100644
--- a/Lib/asyncio/subprocess.py
+++ b/Lib/asyncio/subprocess.py
@@ -144,10 +144,11 @@ class Process:
async def _feed_stdin(self, input):
debug = self._loop.get_debug()
- self.stdin.write(input)
- if debug:
- logger.debug(
- '%r communicate: feed stdin (%s bytes)', self, len(input))
+ if input is not None:
+ self.stdin.write(input)
+ if debug:
+ logger.debug(
+ '%r communicate: feed stdin (%s bytes)', self, len(input))
try:
await self.stdin.drain()
except (BrokenPipeError, ConnectionResetError) as exc:
@@ -180,7 +181,7 @@ class Process:
return output
async def communicate(self, input=None):
- if input is not None:
+ if self.stdin is not None:
stdin = self._feed_stdin(input)
else:
stdin = self._noop()
diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py
index eba6e2d..eeeca40 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -151,6 +151,24 @@ class SubprocessMixin:
self.assertEqual(exitcode, 0)
self.assertEqual(stdout, b'some data')
+ def test_communicate_none_input(self):
+ args = PROGRAM_CAT
+
+ async def run():
+ proc = await asyncio.create_subprocess_exec(
+ *args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
+ stdout, stderr = await proc.communicate()
+ return proc.returncode, stdout
+
+ task = run()
+ task = asyncio.wait_for(task, support.LONG_TIMEOUT)
+ exitcode, stdout = self.loop.run_until_complete(task)
+ self.assertEqual(exitcode, 0)
+ self.assertEqual(stdout, b'')
+
def test_shell(self):
proc = self.loop.run_until_complete(
asyncio.create_subprocess_shell('exit 7')
diff --git a/Misc/NEWS.d/next/Library/2020-02-25-00-43-22.bpo-39744.hgK689.rst b/Misc/NEWS.d/next/Library/2020-02-25-00-43-22.bpo-39744.hgK689.rst
new file mode 100644
index 0000000..6e690f9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-02-25-00-43-22.bpo-39744.hgK689.rst
@@ -0,0 +1 @@
+Make :func:`asyncio.subprocess.Process.communicate` close the subprocess's stdin even when called with ``input=None``.