summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBo Bayles <bbayles@gmail.com>2018-01-30 06:40:39 (GMT)
committerGregory P. Smith <greg@krypto.org>2018-01-30 06:40:39 (GMT)
commitce0f33d04528fcafc673a8707871f8430d8f7ce8 (patch)
treede60362ad2f3c1bb8af239c1926a7f907887e8c1
parent95441809ef77a8df5e14601ade6c054ef7114c02 (diff)
downloadcpython-ce0f33d04528fcafc673a8707871f8430d8f7ce8.zip
cpython-ce0f33d04528fcafc673a8707871f8430d8f7ce8.tar.gz
cpython-ce0f33d04528fcafc673a8707871f8430d8f7ce8.tar.bz2
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.
-rw-r--r--Doc/library/subprocess.rst19
-rw-r--r--Lib/subprocess.py10
-rw-r--r--Lib/test/test_subprocess.py32
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst1
5 files changed, 54 insertions, 9 deletions
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index af96f41..86f3e06 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -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
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index f69159e..93635ee 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -409,7 +409,8 @@ class CompletedProcess(object):
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)
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index dd63818..eee24bb 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1475,6 +1475,38 @@ class RunFuncTestCase(BaseTestCase):
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):
diff --git a/Misc/ACKS b/Misc/ACKS
index bfcdd33..3fdfa30 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -108,6 +108,7 @@ Michael R Bax
Anthony Baxter
Mike Bayer
Samuel L. Bayer
+Bo Bayles
Tommy Beadle
Donald Beaudry
David Beazley
diff --git a/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst
new file mode 100644
index 0000000..cd4d0b5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst
@@ -0,0 +1 @@
+New argument ``capture_output`` for subprocess.run