diff options
author | Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> | 2023-04-28 00:30:26 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-28 00:30:26 (GMT) |
commit | 67d140dba72dc2cb661d55878384464de46719e7 (patch) | |
tree | f9459ce8747f0910eb8e62c9c175801378e3175f | |
parent | 424a785a07049924603228b153f746cfe3a983a2 (diff) | |
download | cpython-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.rst | 9 | ||||
-rw-r--r-- | Lib/asyncio/subprocess.py | 11 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_subprocess.py | 18 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-02-25-00-43-22.bpo-39744.hgK689.rst | 1 |
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``. |