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

Skip to content

Commit afe8d06

Browse files
committed
Issue #21332: Ensure that bufsize=1 in subprocess.Popen() selects line buffering, rather than block buffering.
1 parent 3f40c40 commit afe8d06

4 files changed

Lines changed: 50 additions & 7 deletions

File tree

Doc/library/subprocess.rst

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,18 @@ functions.
406406

407407
Read the `Security Considerations`_ section before using ``shell=True``.
408408

409-
*bufsize* will be supplied as the corresponding argument to the :func:`open`
410-
function when creating the stdin/stdout/stderr pipe file objects: :const:`0`
411-
means unbuffered (read and write are one system call and can return short),
412-
:const:`1` means line buffered, any other positive value means use a buffer
413-
of approximately that size. A negative bufsize (the default) means the
414-
system default of io.DEFAULT_BUFFER_SIZE will be used.
409+
*bufsize* will be supplied as the corresponding argument to the
410+
:func:`open` function when creating the stdin/stdout/stderr pipe
411+
file objects:
412+
413+
- :const:`0` means unbuffered (read and write are one
414+
system call and can return short)
415+
- :const:`1` means line buffered
416+
(only usable if ``universal_newlines=True`` i.e., in a text mode)
417+
- any other positive value means use a buffer of approximately that
418+
size
419+
- negative bufsize (the default) means the system default of
420+
io.DEFAULT_BUFFER_SIZE will be used.
415421

416422
.. versionchanged:: 3.3.1
417423
*bufsize* now defaults to -1 to enable buffering by default to match the

Lib/subprocess.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,8 @@ def __init__(self, args, bufsize=-1, executable=None,
837837
if p2cwrite != -1:
838838
self.stdin = io.open(p2cwrite, 'wb', bufsize)
839839
if universal_newlines:
840-
self.stdin = io.TextIOWrapper(self.stdin, write_through=True)
840+
self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
841+
line_buffering=(bufsize == 1))
841842
if c2pread != -1:
842843
self.stdout = io.open(c2pread, 'rb', bufsize)
843844
if universal_newlines:

Lib/test/test_subprocess.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,39 @@ def test_bufsize_is_none(self):
10081008
p = subprocess.Popen([sys.executable, "-c", "pass"], bufsize=None)
10091009
self.assertEqual(p.wait(), 0)
10101010

1011+
def _test_bufsize_equal_one(self, line, expected, universal_newlines):
1012+
# subprocess may deadlock with bufsize=1, see issue #21332
1013+
with subprocess.Popen([sys.executable, "-c", "import sys;"
1014+
"sys.stdout.write(sys.stdin.readline());"
1015+
"sys.stdout.flush()"],
1016+
stdin=subprocess.PIPE,
1017+
stdout=subprocess.PIPE,
1018+
stderr=subprocess.DEVNULL,
1019+
bufsize=1,
1020+
universal_newlines=universal_newlines) as p:
1021+
p.stdin.write(line) # expect that it flushes the line in text mode
1022+
os.close(p.stdin.fileno()) # close it without flushing the buffer
1023+
read_line = p.stdout.readline()
1024+
try:
1025+
p.stdin.close()
1026+
except OSError:
1027+
pass
1028+
p.stdin = None
1029+
self.assertEqual(p.returncode, 0)
1030+
self.assertEqual(read_line, expected)
1031+
1032+
def test_bufsize_equal_one_text_mode(self):
1033+
# line is flushed in text mode with bufsize=1.
1034+
# we should get the full line in return
1035+
line = "line\n"
1036+
self._test_bufsize_equal_one(line, line, universal_newlines=True)
1037+
1038+
def test_bufsize_equal_one_binary_mode(self):
1039+
# line is not flushed in binary mode with bufsize=1.
1040+
# we should get empty response
1041+
line = b'line' + os.linesep.encode() # assume ascii-based locale
1042+
self._test_bufsize_equal_one(line, b'', universal_newlines=False)
1043+
10111044
def test_leaking_fds_on_error(self):
10121045
# see bug #5179: Popen leaks file descriptors to PIPEs if
10131046
# the child fails to execute; this will eventually exhaust

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- Issue #21332: Ensure that ``bufsize=1`` in subprocess.Popen() selects
36+
line buffering, rather than block buffering. Patch by Akira Li.
37+
3538
- Issue #21091: Fix API bug: email.message.EmailMessage.is_attachment is now
3639
a method. Since EmailMessage is provisional, we can change the API in a
3740
maintenance release, but we use a trick to remain backward compatible with

0 commit comments

Comments
 (0)