Skip to content

Commit 7acfe41

Browse files
bpo-42388: Fix subprocess.check_output input=None when text=True (GH-23467)
When the modern text= spelling of the universal_newlines= parameter was added for Python 3.7, check_output's special case around input=None was overlooked. So it behaved differently with universal_newlines=True vs text=True. This reconciles the behavior to be consistent and adds a test to guarantee it. Also clarifies the existing check_output documentation. Co-authored-by: Alexey Izbyshev <izbyshev@ispras.ru> (cherry picked from commit 64abf37) Co-authored-by: Gregory P. Smith <greg@krypto.org>
1 parent 5a6b5d8 commit 7acfe41

4 files changed

Lines changed: 32 additions & 3 deletions

File tree

Doc/library/subprocess.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,8 +1163,9 @@ calls these functions.
11631163
The arguments shown above are merely some common ones.
11641164
The full function signature is largely the same as that of :func:`run` -
11651165
most arguments are passed directly through to that interface.
1166-
However, explicitly passing ``input=None`` to inherit the parent's
1167-
standard input file handle is not supported.
1166+
One API deviation from :func:`run` behavior exists: passing ``input=None``
1167+
will behave the same as ``input=b''`` (or ``input=''``, depending on other
1168+
arguments) rather than using the parent's standard input file handle.
11681169

11691170
By default, this function will return the data as encoded bytes. The actual
11701171
encoding of the output data may depend on the command being invoked, so the

Lib/subprocess.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,11 @@ def check_output(*popenargs, timeout=None, **kwargs):
415415
if 'input' in kwargs and kwargs['input'] is None:
416416
# Explicitly passing input=None was previously equivalent to passing an
417417
# empty string. That is maintained here for backwards compatibility.
418-
kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b''
418+
if kwargs.get('universal_newlines') or kwargs.get('text'):
419+
empty = ''
420+
else:
421+
empty = b''
422+
kwargs['input'] = empty
419423

420424
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
421425
**kwargs).stdout

Lib/test/test_subprocess.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,28 @@ def test_check_output_input_arg(self):
196196
input=b'pear')
197197
self.assertIn(b'PEAR', output)
198198

199+
def test_check_output_input_none(self):
200+
"""input=None has a legacy meaning of input='' on check_output."""
201+
output = subprocess.check_output(
202+
[sys.executable, "-c",
203+
"import sys; print('XX' if sys.stdin.read() else '')"],
204+
input=None)
205+
self.assertNotIn(b'XX', output)
206+
207+
def test_check_output_input_none_text(self):
208+
output = subprocess.check_output(
209+
[sys.executable, "-c",
210+
"import sys; print('XX' if sys.stdin.read() else '')"],
211+
input=None, text=True)
212+
self.assertNotIn('XX', output)
213+
214+
def test_check_output_input_none_universal_newlines(self):
215+
output = subprocess.check_output(
216+
[sys.executable, "-c",
217+
"import sys; print('XX' if sys.stdin.read() else '')"],
218+
input=None, universal_newlines=True)
219+
self.assertNotIn('XX', output)
220+
199221
def test_check_output_stdout_arg(self):
200222
# check_output() refuses to accept 'stdout' argument
201223
with self.assertRaises(ValueError) as c:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix subprocess.check_output(..., input=None) behavior when text=True to be
2+
consistent with that of the documentation and universal_newlines=True.

0 commit comments

Comments
 (0)