mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
Issue #16624: subprocess.check_output
now accepts an input
argument,
allowing the subprocess's stdin to be provided as a (byte) string. Patch by Zack Weinberg.
This commit is contained in:
parent
1859fe80c4
commit
fcd9f22238
4 changed files with 78 additions and 10 deletions
|
@ -116,7 +116,7 @@ use cases, the underlying :class:`Popen` interface can be used directly.
|
|||
*timeout* was added.
|
||||
|
||||
|
||||
.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
|
||||
.. function:: check_output(args, *, input=None, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
|
||||
|
||||
Run command with arguments and return its output.
|
||||
|
||||
|
@ -129,15 +129,21 @@ use cases, the underlying :class:`Popen` interface can be used directly.
|
|||
in :ref:`frequently-used-arguments` (hence the use of keyword-only notation
|
||||
in the abbreviated signature). The full function signature is largely the
|
||||
same as that of the :class:`Popen` constructor - this functions passes all
|
||||
supplied arguments other than *timeout* directly through to that interface.
|
||||
In addition, *stdout* is not permitted as an argument, as it is used
|
||||
internally to collect the output from the subprocess.
|
||||
supplied arguments other than *input* and *timeout* directly through to
|
||||
that interface. In addition, *stdout* is not permitted as an argument, as
|
||||
it is used internally to collect the output from the subprocess.
|
||||
|
||||
The *timeout* argument is passed to :meth:`Popen.wait`. If the timeout
|
||||
expires, the child process will be killed and then waited for again. The
|
||||
:exc:`TimeoutExpired` exception will be re-raised after the child process
|
||||
has terminated.
|
||||
|
||||
The *input* argument is passed to :meth:`Popen.communicate` and thus to the
|
||||
subprocess's stdin. If used it must be a byte sequence, or a string if
|
||||
``universal_newlines=True``. When used, the internal :class:`Popen` object
|
||||
is automatically created with ``stdin=PIPE``, and the *stdin* argument may
|
||||
not be used as well.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> subprocess.check_output(["echo", "Hello World!"])
|
||||
|
@ -146,6 +152,10 @@ use cases, the underlying :class:`Popen` interface can be used directly.
|
|||
>>> subprocess.check_output(["echo", "Hello World!"], universal_newlines=True)
|
||||
'Hello World!\n'
|
||||
|
||||
>>> subprocess.check_output(["sed", "-e", "s/foo/bar/"],
|
||||
... input=b"when in the course of fooman events\n")
|
||||
b'when in the course of barman events\n'
|
||||
|
||||
>>> subprocess.check_output("exit 1", shell=True)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
|
@ -167,10 +177,6 @@ use cases, the underlying :class:`Popen` interface can be used directly.
|
|||
... shell=True)
|
||||
'ls: non_existent_file: No such file or directory\n'
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
..
|
||||
|
||||
.. warning::
|
||||
|
||||
Invoking the system shell with ``shell=True`` can be a security hazard
|
||||
|
@ -183,9 +189,13 @@ use cases, the underlying :class:`Popen` interface can be used directly.
|
|||
read in the current process, the child process may block if it
|
||||
generates enough output to the pipe to fill up the OS pipe buffer.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
*timeout* was added.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
*input* was added.
|
||||
|
||||
.. data:: DEVNULL
|
||||
|
||||
|
|
|
@ -175,6 +175,9 @@ check_output(*popenargs, **kwargs):
|
|||
|
||||
>>> output = subprocess.check_output(["ls", "-l", "/dev/null"])
|
||||
|
||||
There is an additional optional argument, "input", allowing you to
|
||||
pass a string to the subprocess's stdin. If you use this argument
|
||||
you may not also use the Popen constructor's "stdin" argument.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
@ -563,14 +566,31 @@ def check_output(*popenargs, timeout=None, **kwargs):
|
|||
... stderr=STDOUT)
|
||||
b'ls: non_existent_file: No such file or directory\n'
|
||||
|
||||
There is an additional optional argument, "input", allowing you to
|
||||
pass a string to the subprocess's stdin. If you use this argument
|
||||
you may not also use the Popen constructor's "stdin" argument, as
|
||||
it too will be used internally. Example:
|
||||
|
||||
>>> check_output(["sed", "-e", "s/foo/bar/"],
|
||||
... input=b"when in the course of fooman events\n")
|
||||
b'when in the course of barman events\n'
|
||||
|
||||
If universal_newlines=True is passed, the return value will be a
|
||||
string rather than bytes.
|
||||
"""
|
||||
if 'stdout' in kwargs:
|
||||
raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||
if 'input' in kwargs:
|
||||
if 'stdin' in kwargs:
|
||||
raise ValueError('stdin and input arguments may not both be used.')
|
||||
inputdata = kwargs['input']
|
||||
del kwargs['input']
|
||||
kwargs['stdin'] = PIPE
|
||||
else:
|
||||
inputdata = None
|
||||
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
|
||||
try:
|
||||
output, unused_err = process.communicate(timeout=timeout)
|
||||
output, unused_err = process.communicate(inputdata, timeout=timeout)
|
||||
except TimeoutExpired:
|
||||
process.kill()
|
||||
output, unused_err = process.communicate()
|
||||
|
|
|
@ -158,8 +158,28 @@ class ProcessTestCase(BaseTestCase):
|
|||
stderr=subprocess.STDOUT)
|
||||
self.assertIn(b'BDFL', output)
|
||||
|
||||
def test_check_output_stdin_arg(self):
|
||||
# check_output() can be called with stdin set to a file
|
||||
tf = tempfile.TemporaryFile()
|
||||
self.addCleanup(tf.close)
|
||||
tf.write(b'pear')
|
||||
tf.seek(0)
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c",
|
||||
"import sys; sys.stdout.write(sys.stdin.read().upper())"],
|
||||
stdin=tf)
|
||||
self.assertIn(b'PEAR', output)
|
||||
|
||||
def test_check_output_input_arg(self):
|
||||
# check_output() can be called with input set to a string
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c",
|
||||
"import sys; sys.stdout.write(sys.stdin.read().upper())"],
|
||||
input=b'pear')
|
||||
self.assertIn(b'PEAR', output)
|
||||
|
||||
def test_check_output_stdout_arg(self):
|
||||
# check_output() function stderr redirected to stdout
|
||||
# check_output() refuses to accept 'stdout' argument
|
||||
with self.assertRaises(ValueError) as c:
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c", "print('will not be run')"],
|
||||
|
@ -167,6 +187,20 @@ class ProcessTestCase(BaseTestCase):
|
|||
self.fail("Expected ValueError when stdout arg supplied.")
|
||||
self.assertIn('stdout', c.exception.args[0])
|
||||
|
||||
def test_check_output_stdin_with_input_arg(self):
|
||||
# check_output() refuses to accept 'stdin' with 'input'
|
||||
tf = tempfile.TemporaryFile()
|
||||
self.addCleanup(tf.close)
|
||||
tf.write(b'pear')
|
||||
tf.seek(0)
|
||||
with self.assertRaises(ValueError) as c:
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c", "print('will not be run')"],
|
||||
stdin=tf, input=b'hare')
|
||||
self.fail("Expected ValueError when stdin and input args supplied.")
|
||||
self.assertIn('stdin', c.exception.args[0])
|
||||
self.assertIn('input', c.exception.args[0])
|
||||
|
||||
def test_check_output_timeout(self):
|
||||
# check_output() function with timeout arg
|
||||
with self.assertRaises(subprocess.TimeoutExpired) as c:
|
||||
|
|
|
@ -49,6 +49,10 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #16624: `subprocess.check_output` now accepts an `input` argument,
|
||||
allowing the subprocess's stdin to be provided as a (byte) string.
|
||||
Patch by Zack Weinberg.
|
||||
|
||||
- Issue #17795: Reverted backwards-incompatible change in SysLogHandler with
|
||||
Unix domain sockets.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue