Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5ef586f

Browse files
committed
Closes #22685, asyncio: Set the transport of stdout and stderr StreamReader
objects in the SubprocessStreamProtocol. It allows to pause the transport to not buffer too much stdout or stderr data.
1 parent c8c64e3 commit 5ef586f

2 files changed

Lines changed: 44 additions & 5 deletions

File tree

Lib/asyncio/subprocess.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,22 @@ def __repr__(self):
4141

4242
def connection_made(self, transport):
4343
self._transport = transport
44-
if transport.get_pipe_transport(1):
44+
45+
stdout_transport = transport.get_pipe_transport(1)
46+
if stdout_transport is not None:
4547
self.stdout = streams.StreamReader(limit=self._limit,
4648
loop=self._loop)
47-
if transport.get_pipe_transport(2):
49+
self.stdout.set_transport(stdout_transport)
50+
51+
stderr_transport = transport.get_pipe_transport(2)
52+
if stderr_transport is not None:
4853
self.stderr = streams.StreamReader(limit=self._limit,
4954
loop=self._loop)
50-
stdin = transport.get_pipe_transport(0)
51-
if stdin is not None:
52-
self.stdin = streams.StreamWriter(stdin,
55+
self.stderr.set_transport(stderr_transport)
56+
57+
stdin_transport = transport.get_pipe_transport(0)
58+
if stdin_transport is not None:
59+
self.stdin = streams.StreamWriter(stdin_transport,
5360
protocol=self,
5461
reader=None,
5562
loop=self._loop)

Lib/test/test_asyncio/test_subprocess.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import signal
55
import sys
66
import unittest
7+
from unittest import mock
78
from test import support
89
if sys.platform != 'win32':
910
from asyncio import unix_events
@@ -161,6 +162,37 @@ def test_communicate_ignore_broken_pipe(self):
161162
self.loop.run_until_complete(proc.communicate(large_data))
162163
self.loop.run_until_complete(proc.wait())
163164

165+
def test_pause_reading(self):
166+
@asyncio.coroutine
167+
def test_pause_reading():
168+
limit = 100
169+
170+
code = '\n'.join((
171+
'import sys',
172+
'sys.stdout.write("x" * %s)' % (limit * 2 + 1),
173+
'sys.stdout.flush()',
174+
))
175+
proc = yield from asyncio.create_subprocess_exec(
176+
sys.executable, '-c', code,
177+
stdin=asyncio.subprocess.PIPE,
178+
stdout=asyncio.subprocess.PIPE,
179+
limit=limit,
180+
loop=self.loop)
181+
stdout_transport = proc._transport.get_pipe_transport(1)
182+
stdout_transport.pause_reading = mock.Mock()
183+
184+
yield from proc.wait()
185+
186+
# The child process produced more than limit bytes of output,
187+
# the stream reader transport should pause the protocol to not
188+
# allocate too much memory.
189+
return stdout_transport.pause_reading.called
190+
191+
# Issue #22685: Ensure that the stream reader pauses the protocol
192+
# when the child process produces too much data
193+
called = self.loop.run_until_complete(test_pause_reading())
194+
self.assertTrue(called)
195+
164196

165197
if sys.platform != 'win32':
166198
# Unix

0 commit comments

Comments
 (0)