diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index a93a6c186f8d803..7ce274652d394aa 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1127,8 +1127,9 @@ calls these functions. The arguments shown above are merely some common ones. The full function signature is largely the same as that of :func:`run` - most arguments are passed directly through to that interface. - However, explicitly passing ``input=None`` to inherit the parent's - standard input file handle is not supported. + One API deviation from :func:`run` behavior exists: passing ``input=None`` + will behave the same as ``input=b''`` (or ``input=''``, depending on other + arguments) rather than using the parent's standard input file handle. By default, this function will return the data as encoded bytes. The actual encoding of the output data may depend on the command being invoked, so the diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 5c2c2f05093f7e3..eecb1e7f253fbaf 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -406,7 +406,11 @@ def check_output(*popenargs, timeout=None, **kwargs): if 'input' in kwargs and kwargs['input'] is None: # Explicitly passing input=None was previously equivalent to passing an # empty string. That is maintained here for backwards compatibility. - kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b'' + if kwargs.get('universal_newlines') or kwargs.get('text'): + empty = '' + else: + empty = b'' + kwargs['input'] = empty return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, **kwargs).stdout diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 2d3ab93555b4c3e..4661d1e25280afc 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -195,6 +195,28 @@ def test_check_output_input_arg(self): input=b'pear') self.assertIn(b'PEAR', output) + def test_check_output_input_none(self): + """input=None has a legacy meaning of input='' on check_output.""" + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None) + self.assertNotIn(b'XX', output) + + def test_check_output_input_none_text(self): + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None, text=True) + self.assertNotIn('XX', output) + + def test_check_output_input_none_universal_newlines(self): + output = subprocess.check_output( + [sys.executable, "-c", + "import sys; print('XX' if sys.stdin.read() else '')"], + input=None, universal_newlines=True) + self.assertNotIn('XX', output) + def test_check_output_stdout_arg(self): # check_output() refuses to accept 'stdout' argument with self.assertRaises(ValueError) as c: diff --git a/Misc/NEWS.d/next/Library/2020-11-22-11-22-28.bpo-42388.LMgM6B.rst b/Misc/NEWS.d/next/Library/2020-11-22-11-22-28.bpo-42388.LMgM6B.rst new file mode 100644 index 000000000000000..1b19247e8414878 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-22-11-22-28.bpo-42388.LMgM6B.rst @@ -0,0 +1,2 @@ +Fix subprocess.check_output(..., input=None) behavior when text=True to be +consistent with that of the documentation and universal_newlines=True.