summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2014-09-21 19:15:42 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2014-09-21 19:15:42 (GMT)
commit6e311aa748b123b1f1333c2c86b5904b57168276 (patch)
tree0577b6fab7a1380f5e552ba846d0fb108b5e57e2 /Lib
parent9e7fbde67f55e8e5c21bc41568c7fe13e72b44bc (diff)
parentafe8d0646c9347960e99f5373d6cbd712b5376bb (diff)
downloadcpython-6e311aa748b123b1f1333c2c86b5904b57168276.zip
cpython-6e311aa748b123b1f1333c2c86b5904b57168276.tar.gz
cpython-6e311aa748b123b1f1333c2c86b5904b57168276.tar.bz2
Issue #21332: Ensure that ``bufsize=1`` in subprocess.Popen() selects line buffering, rather than block buffering.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/subprocess.py3
-rw-r--r--Lib/test/test_subprocess.py33
2 files changed, 35 insertions, 1 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index bb7d0dc..3ce0ed1 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -841,7 +841,8 @@ class Popen(object):
if p2cwrite != -1:
self.stdin = io.open(p2cwrite, 'wb', bufsize)
if universal_newlines:
- self.stdin = io.TextIOWrapper(self.stdin, write_through=True)
+ self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
+ line_buffering=(bufsize == 1))
if c2pread != -1:
self.stdout = io.open(c2pread, 'rb', bufsize)
if universal_newlines:
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index d0ab718..9616da0 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1008,6 +1008,39 @@ class ProcessTestCase(BaseTestCase):
p = subprocess.Popen([sys.executable, "-c", "pass"], bufsize=None)
self.assertEqual(p.wait(), 0)
+ def _test_bufsize_equal_one(self, line, expected, universal_newlines):
+ # subprocess may deadlock with bufsize=1, see issue #21332
+ with subprocess.Popen([sys.executable, "-c", "import sys;"
+ "sys.stdout.write(sys.stdin.readline());"
+ "sys.stdout.flush()"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ bufsize=1,
+ universal_newlines=universal_newlines) as p:
+ p.stdin.write(line) # expect that it flushes the line in text mode
+ os.close(p.stdin.fileno()) # close it without flushing the buffer
+ read_line = p.stdout.readline()
+ try:
+ p.stdin.close()
+ except OSError:
+ pass
+ p.stdin = None
+ self.assertEqual(p.returncode, 0)
+ self.assertEqual(read_line, expected)
+
+ def test_bufsize_equal_one_text_mode(self):
+ # line is flushed in text mode with bufsize=1.
+ # we should get the full line in return
+ line = "line\n"
+ self._test_bufsize_equal_one(line, line, universal_newlines=True)
+
+ def test_bufsize_equal_one_binary_mode(self):
+ # line is not flushed in binary mode with bufsize=1.
+ # we should get empty response
+ line = b'line' + os.linesep.encode() # assume ascii-based locale
+ self._test_bufsize_equal_one(line, b'', universal_newlines=False)
+
def test_leaking_fds_on_error(self):
# see bug #5179: Popen leaks file descriptors to PIPEs if
# the child fails to execute; this will eventually exhaust