Kaydet (Commit) fcd9f222 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka

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.
üst 1859fe80
......@@ -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.
......
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