Adds a subprocess.check_call_output() function to return the output from a
process on success or raise an exception on error.
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index 42e50f6..468892a 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -149,6 +149,30 @@
.. versionadded:: 2.5
+.. function:: check_call_output(*popenargs, **kwargs)
+
+ Run command with arguments and return its output as a byte string.
+
+ If the exit code was non-zero it raises a CalledProcessError. The
+ CalledProcessError object will have the return code in the returncode
+ attribute and output in the output attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ >>> subprocess.check_call_output(["ls", "-l", "/dev/null"])
+ 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
+
+ The stdout argument is not allowed as it is used internally.
+ To capture standard error in the result, use stderr=subprocess.STDOUT.
+
+ >>> subprocess.check_call_output(
+ ["/bin/sh", "-c", "ls non_existant_file ; exit 0"],
+ stderr=subprocess.STDOUT)
+ 'ls: non_existant_file: No such file or directory\n'
+
+ .. versionadded:: 2.7
+
+
Exceptions
^^^^^^^^^^
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 935827a..7caf528 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -107,7 +107,7 @@
(Windows only)
-This module also defines two shortcut functions:
+This module also defines some shortcut functions:
call(*popenargs, **kwargs):
Run command with arguments. Wait for command to complete, then
@@ -127,6 +127,17 @@
check_call(["ls", "-l"])
+check_call_output(*popenargs, **kwargs):
+ Run command with arguments and return its output as a byte string.
+
+ If the exit code was non-zero it raises a CalledProcessError. The
+ CalledProcessError object will have the return code in the returncode
+ attribute and output in the output attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ output = subprocess.check_call_output(["ls", "-l", "/dev/null"])
+
Exceptions
----------
Exceptions raised in the child process, before the new program has
@@ -141,8 +152,8 @@
A ValueError will be raised if Popen is called with invalid arguments.
-check_call() will raise CalledProcessError, if the called process
-returns a non-zero return code.
+check_call() and check_call_output() will raise CalledProcessError, if the
+called process returns a non-zero return code.
Security
@@ -361,12 +372,15 @@
# Exception classes used by this module.
class CalledProcessError(Exception):
- """This exception is raised when a process run by check_call() returns
- a non-zero exit status. The exit status will be stored in the
- returncode attribute."""
- def __init__(self, returncode, cmd):
+ """This exception is raised when a process run by check_call() or
+ check_call_output() returns a non-zero exit status.
+ The exit status will be stored in the returncode attribute;
+ check_call_output() will also store the output in the output attribute.
+ """
+ def __init__(self, returncode, cmd, output=None):
self.returncode = returncode
self.cmd = cmd
+ self.output = output
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
@@ -403,7 +417,8 @@
import fcntl
import pickle
-__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
+__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call",
+ "check_call_output", "CalledProcessError"]
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
@@ -455,12 +470,45 @@
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
raise CalledProcessError(retcode, cmd)
- return retcode
+ return 0
+
+
+def check_call_output(*popenargs, **kwargs):
+ """Run command with arguments and return its output as a byte string.
+
+ If the exit code was non-zero it raises a CalledProcessError. The
+ CalledProcessError object will have the return code in the returncode
+ attribute and output in the output attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ >>> check_call_output(["ls", "-l", "/dev/null"])
+ 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
+
+ The stdout argument is not allowed as it is used internally.
+ To capture standard error in the result, use stderr=subprocess.STDOUT.
+
+ >>> check_call_output(["/bin/sh", "-c",
+ "ls -l non_existant_file ; exit 0"],
+ stderr=subprocess.STDOUT)
+ 'ls: non_existant_file: No such file or directory\n'
+ """
+ if 'stdout' in kwargs:
+ raise ValueError('stdout argument not allowed, it will be overridden.')
+ process = Popen(*popenargs, stdout=PIPE, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ raise CalledProcessError(retcode, cmd, output=output)
+ return output
def list2cmdline(seq):
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index e7ba26f..878b79b 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -72,6 +72,40 @@
else:
self.fail("Expected CalledProcessError")
+ def test_check_call_output(self):
+ # check_call_output() function with zero return code
+ output = subprocess.check_call_output(
+ [sys.executable, "-c", "print 'BDFL'"])
+ self.assertTrue('BDFL' in output)
+
+ def test_check_call_output_nonzero(self):
+ # check_call() function with non-zero return code
+ try:
+ subprocess.check_call_output(
+ [sys.executable, "-c", "import sys; sys.exit(5)"])
+ except subprocess.CalledProcessError, e:
+ self.assertEqual(e.returncode, 5)
+ else:
+ self.fail("Expected CalledProcessError")
+
+ def test_check_call_output_stderr(self):
+ # check_call_output() function stderr redirected to stdout
+ output = subprocess.check_call_output(
+ [sys.executable, "-c", "import sys; sys.stderr.write('BDFL')"],
+ stderr=subprocess.STDOUT)
+ self.assertTrue('BDFL' in output)
+
+ def test_check_call_output_stdout_arg(self):
+ # check_call_output() function stderr redirected to stdout
+ try:
+ output = subprocess.check_call_output(
+ [sys.executable, "-c", "print 'will not be run'"],
+ stdout=sys.stdout)
+ except ValueError, e:
+ self.assertTrue('stdout' in e.args[0])
+ else:
+ self.fail("Expected ValueError when stdout arg supplied.")
+
def test_call_kwargs(self):
# call() function with keyword args
newenv = os.environ.copy()
diff --git a/Misc/NEWS b/Misc/NEWS
index 300c819..7905795 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -60,6 +60,9 @@
Library
-------
+- Added the subprocess.check_call_output() convenience function to get output
+ from a subprocess on success or raise an exception on error.
+
- Issue #1055234: cgi.parse_header(): Fixed parsing of header parameters to
support unusual filenames (such as those containing semi-colons) in
Content-Disposition headers.