Commit 514cfbde authored by Gregory P. Smith's avatar Gregory P. Smith

Fixes issue #17488: Change the subprocess.Popen bufsize parameter default value

from unbuffered (0) to buffering (-1) to match the behavior existing code
expects and match the behavior of the subprocess module in Python 2 to avoid
introducing hard to track down bugs.
parent b19321cb
...@@ -284,7 +284,7 @@ are able to handle the less common cases not covered by the convenience ...@@ -284,7 +284,7 @@ are able to handle the less common cases not covered by the convenience
functions. functions.
.. class:: Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, \ .. class:: Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, \
stderr=None, preexec_fn=None, close_fds=True, shell=False, \ stderr=None, preexec_fn=None, close_fds=True, shell=False, \
cwd=None, env=None, universal_newlines=False, \ cwd=None, env=None, universal_newlines=False, \
startupinfo=None, creationflags=0, restore_signals=True, \ startupinfo=None, creationflags=0, restore_signals=True, \
...@@ -356,17 +356,20 @@ functions. ...@@ -356,17 +356,20 @@ functions.
untrusted input. See the warning under :ref:`frequently-used-arguments` untrusted input. See the warning under :ref:`frequently-used-arguments`
for details. for details.
*bufsize*, if given, has the same meaning as the corresponding argument to the *bufsize* will be supplied as the corresponding argument to the :meth:`io.open`
built-in open() function: :const:`0` means unbuffered, :const:`1` means line function when creating the stdin/stdout/stderr pipe file objects:
buffered, any other positive value means use a buffer of (approximately) that :const:`0` means unbuffered (read and write are one system call and can return short),
size. A negative *bufsize* means to use the system default, which usually means :const:`1` means line buffered, any other positive value means use a buffer of
fully buffered. The default value for *bufsize* is :const:`0` (unbuffered). approximately that size. A negative bufsize (the default) means
the system default of io.DEFAULT_BUFFER_SIZE will be used.
.. note:: .. versionchanged:: 3.2.4
If you experience performance issues, it is recommended that you try to *bufsize* now defaults to -1 to enable buffering by default to match the
enable buffering by setting *bufsize* to either -1 or a large enough behavior that most code expects. In 3.2.0 through 3.2.3 it incorrectly
positive value (such as 4096). defaulted to :const:`0` which was unbuffered and allowed short reads.
This was unintentional and did not match the behavior of Python 2 as
most code expected.
The *executable* argument specifies a replacement program to execute. It The *executable* argument specifies a replacement program to execute. It
is very seldom needed. When ``shell=False``, *executable* replaces the is very seldom needed. When ``shell=False``, *executable* replaces the
......
...@@ -25,7 +25,7 @@ Using the subprocess module ...@@ -25,7 +25,7 @@ Using the subprocess module
=========================== ===========================
This module defines one class called Popen: This module defines one class called Popen:
class Popen(args, bufsize=0, executable=None, class Popen(args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None, stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True, shell=False, preexec_fn=None, close_fds=True, shell=False,
cwd=None, env=None, universal_newlines=False, cwd=None, env=None, universal_newlines=False,
...@@ -56,12 +56,12 @@ not all MS Windows applications interpret the command line the same ...@@ -56,12 +56,12 @@ not all MS Windows applications interpret the command line the same
way: The list2cmdline is designed for applications using the same way: The list2cmdline is designed for applications using the same
rules as the MS C runtime. rules as the MS C runtime.
bufsize, if given, has the same meaning as the corresponding argument bufsize will be supplied as the corresponding argument to the io.open()
to the built-in open() function: 0 means unbuffered, 1 means line function when creating the stdin/stdout/stderr pipe file objects:
buffered, any other positive value means use a buffer of 0 means unbuffered (read & write are one system call and can return short),
(approximately) that size. A negative bufsize means to use the system 1 means line buffered, any other positive value means use a buffer of
default, which usually means fully buffered. The default value for approximately that size. A negative bufsize, the default, means the system
bufsize is 0 (unbuffered). default of io.DEFAULT_BUFFER_SIZE will be used.
stdin, stdout and stderr specify the executed programs' standard stdin, stdout and stderr specify the executed programs' standard
input, standard output and standard error file handles, respectively. input, standard output and standard error file handles, respectively.
...@@ -638,7 +638,7 @@ _PLATFORM_DEFAULT_CLOSE_FDS = object() ...@@ -638,7 +638,7 @@ _PLATFORM_DEFAULT_CLOSE_FDS = object()
class Popen(object): class Popen(object):
def __init__(self, args, bufsize=0, executable=None, def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None, stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS, preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
shell=False, cwd=None, env=None, universal_newlines=False, shell=False, cwd=None, env=None, universal_newlines=False,
...@@ -650,7 +650,7 @@ class Popen(object): ...@@ -650,7 +650,7 @@ class Popen(object):
self._child_created = False self._child_created = False
if bufsize is None: if bufsize is None:
bufsize = 0 # Restore default bufsize = -1 # Restore default
if not isinstance(bufsize, int): if not isinstance(bufsize, int):
raise TypeError("bufsize must be an integer") raise TypeError("bufsize must be an integer")
......
...@@ -79,6 +79,28 @@ class PopenExecuteChildRaises(subprocess.Popen): ...@@ -79,6 +79,28 @@ class PopenExecuteChildRaises(subprocess.Popen):
class ProcessTestCase(BaseTestCase): class ProcessTestCase(BaseTestCase):
def test_io_buffered_by_default(self):
p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
self.assertIsInstance(p.stdin, io.BufferedIOBase)
self.assertIsInstance(p.stdout, io.BufferedIOBase)
self.assertIsInstance(p.stderr, io.BufferedIOBase)
finally:
p.wait()
def test_io_unbuffered_works(self):
p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, bufsize=0)
try:
self.assertIsInstance(p.stdin, io.RawIOBase)
self.assertIsInstance(p.stdout, io.RawIOBase)
self.assertIsInstance(p.stderr, io.RawIOBase)
finally:
p.wait()
def test_call_seq(self): def test_call_seq(self):
# call() function with sequence argument # call() function with sequence argument
rc = subprocess.call([sys.executable, "-c", rc = subprocess.call([sys.executable, "-c",
......
...@@ -233,6 +233,11 @@ Core and Builtins ...@@ -233,6 +233,11 @@ Core and Builtins
Library Library
------- -------
- Issue #17488: Change the subprocess.Popen bufsize parameter default value
from unbuffered (0) to buffering (-1) to match the behavior existing code
expects and match the behavior of the subprocess module in Python 2 to avoid
introducing hard to track down bugs.
- Issue #17521: Corrected non-enabling of logger following two calls to - Issue #17521: Corrected non-enabling of logger following two calls to
fileConfig(). fileConfig().
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment