Adds a subprocess.check_call_output() function to return the output from a

process on success or raise an exception on error.
This commit is contained in:
Gregory P. Smith 2008-12-04 20:21:09 +00:00
parent 32d1408192
commit 97f49f4be7
4 changed files with 121 additions and 12 deletions

View File

@ -149,6 +149,30 @@ This module also defines two shortcut functions:
.. 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
^^^^^^^^^^

View File

@ -107,7 +107,7 @@ appearance of the main window and priority for the new process.
(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(*popenargs, **kwargs):
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 @@ should prepare for OSErrors.
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 @@ import signal
# 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 @@ else:
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 @@ def check_call(*popenargs, **kwargs):
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):

View File

@ -72,6 +72,40 @@ class ProcessTestCase(unittest.TestCase):
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()

View File

@ -60,6 +60,9 @@ Core and Builtins
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.