mirror of
https://github.com/python/cpython.git
synced 2026-01-06 15:32:22 +00:00
bpo-32102 Add "capture_output=True" to subprocess.run (GH-5149)
Add "capture_output=True" option to subprocess.run, this is equivalent to setting stdout=PIPE, stderr=PIPE but is much more readable.
This commit is contained in:
parent
95441809ef
commit
ce0f33d045
5 changed files with 54 additions and 9 deletions
|
|
@ -47,12 +47,14 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
|
|||
The arguments shown above are merely the most common ones, described below
|
||||
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 - apart from *timeout*,
|
||||
*input* and *check*, all the arguments to this function are passed through to
|
||||
that interface.
|
||||
same as that of the :class:`Popen` constructor - most of the arguments to
|
||||
this function are passed through to that interface. (*timeout*, *input*,
|
||||
*check*, and *capture_output* are not.)
|
||||
|
||||
This does not capture stdout or stderr by default. To do so, pass
|
||||
:data:`PIPE` for the *stdout* and/or *stderr* arguments.
|
||||
If *capture_output* is true, stdout and stderr will be captured.
|
||||
When used, the internal :class:`Popen` object is automatically created with
|
||||
``stdout=PIPE`` and ``stderr=PIPE``. The *stdout* and *stderr* arguments may
|
||||
not be used as well.
|
||||
|
||||
The *timeout* argument is passed to :meth:`Popen.communicate`. If the timeout
|
||||
expires, the child process will be killed and waited for. The
|
||||
|
|
@ -86,9 +88,9 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
|
|||
...
|
||||
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
|
||||
|
||||
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
|
||||
>>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
|
||||
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
|
||||
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
|
||||
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'')
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
|
|
@ -98,7 +100,8 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
|
|||
|
||||
.. versionchanged:: 3.7
|
||||
|
||||
Added the *text* parameter, as a more understandable alias of *universal_newlines*
|
||||
Added the *text* parameter, as a more understandable alias of *universal_newlines*.
|
||||
Added the *capture_output* parameter.
|
||||
|
||||
.. class:: CompletedProcess
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,8 @@ def check_returncode(self):
|
|||
self.stderr)
|
||||
|
||||
|
||||
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
|
||||
def run(*popenargs,
|
||||
input=None, capture_output=False, timeout=None, check=False, **kwargs):
|
||||
"""Run command with arguments and return a CompletedProcess instance.
|
||||
|
||||
The returned instance will have attributes args, returncode, stdout and
|
||||
|
|
@ -442,6 +443,13 @@ def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
|
|||
raise ValueError('stdin and input arguments may not both be used.')
|
||||
kwargs['stdin'] = PIPE
|
||||
|
||||
if capture_output:
|
||||
if ('stdout' in kwargs) or ('stderr' in kwargs):
|
||||
raise ValueError('stdout and stderr arguments may not be used '
|
||||
'with capture_output.')
|
||||
kwargs['stdout'] = PIPE
|
||||
kwargs['stderr'] = PIPE
|
||||
|
||||
with Popen(*popenargs, **kwargs) as process:
|
||||
try:
|
||||
stdout, stderr = process.communicate(input, timeout=timeout)
|
||||
|
|
|
|||
|
|
@ -1475,6 +1475,38 @@ def test_run_kwargs(self):
|
|||
env=newenv)
|
||||
self.assertEqual(cp.returncode, 33)
|
||||
|
||||
def test_capture_output(self):
|
||||
cp = self.run_python(("import sys;"
|
||||
"sys.stdout.write('BDFL'); "
|
||||
"sys.stderr.write('FLUFL')"),
|
||||
capture_output=True)
|
||||
self.assertIn(b'BDFL', cp.stdout)
|
||||
self.assertIn(b'FLUFL', cp.stderr)
|
||||
|
||||
def test_stdout_with_capture_output_arg(self):
|
||||
# run() refuses to accept 'stdout' with 'capture_output'
|
||||
tf = tempfile.TemporaryFile()
|
||||
self.addCleanup(tf.close)
|
||||
with self.assertRaises(ValueError,
|
||||
msg=("Expected ValueError when stdout and capture_output "
|
||||
"args supplied.")) as c:
|
||||
output = self.run_python("print('will not be run')",
|
||||
capture_output=True, stdout=tf)
|
||||
self.assertIn('stdout', c.exception.args[0])
|
||||
self.assertIn('capture_output', c.exception.args[0])
|
||||
|
||||
def test_stderr_with_capture_output_arg(self):
|
||||
# run() refuses to accept 'stderr' with 'capture_output'
|
||||
tf = tempfile.TemporaryFile()
|
||||
self.addCleanup(tf.close)
|
||||
with self.assertRaises(ValueError,
|
||||
msg=("Expected ValueError when stderr and capture_output "
|
||||
"args supplied.")) as c:
|
||||
output = self.run_python("print('will not be run')",
|
||||
capture_output=True, stderr=tf)
|
||||
self.assertIn('stderr', c.exception.args[0])
|
||||
self.assertIn('capture_output', c.exception.args[0])
|
||||
|
||||
|
||||
@unittest.skipIf(mswindows, "POSIX specific tests")
|
||||
class POSIXProcessTestCase(BaseTestCase):
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ Michael R Bax
|
|||
Anthony Baxter
|
||||
Mike Bayer
|
||||
Samuel L. Bayer
|
||||
Bo Bayles
|
||||
Tommy Beadle
|
||||
Donald Beaudry
|
||||
David Beazley
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
New argument ``capture_output`` for subprocess.run
|
||||
Loading…
Add table
Add a link
Reference in a new issue