Index: subprocess.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/subprocess.py,v retrieving revision 1.8 diff -u -r1.8 subprocess.py --- subprocess.py 7 Nov 2004 14:30:34 -0000 1.8 +++ subprocess.py 23 Nov 2004 13:54:48 -0000 @@ -123,7 +123,7 @@ (Windows only) -This module also defines two shortcut functions: +This module also defines three shortcut functions: call(*args, **kwargs): Run command with arguments. Wait for command to complete, then @@ -133,6 +133,15 @@ retcode = call(["ls", "-l"]) +xcall(*args, **kwargs): + Run command with arguments. Wait for command to complete. If the + exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the errno attribute. + + The arguments are the same as for the Popen constructor. Example: + + xcall(["ls", "-l"]) Exceptions ---------- @@ -148,6 +157,9 @@ A ValueError will be raised if Popen is called with invalid arguments. +xcall() will raise CalledProcessError which is a child of OSError if +the called process returns a non-zero return code. + Security -------- @@ -363,6 +375,13 @@ import types import traceback +# Exception classes used by this module. +class CalledProcessError(OSError): + """This exception is raised when a process run by xcall() returns + a non-zero exit status. The exit status will be stored in the + errno attribute. Note that this exception is a subclass of + OSError.""" + if mswindows: import threading import msvcrt @@ -393,7 +412,7 @@ import fcntl import pickle -__all__ = ["Popen", "PIPE", "STDOUT", "call"] +__all__ = ["Popen", "PIPE", "STDOUT", "call", "xcall", "CalledProcessError"] try: MAXFD = os.sysconf("SC_OPEN_MAX") @@ -428,6 +447,21 @@ return Popen(*args, **kwargs).wait() +def xcall(*args, **kwargs): + """Run command with arguments. Wait for command to complete. If + the exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the errno attribute. + + The arguments are the same as for the Popen constructor. Example: + + xcall(["ls", "-l"]) + """ + rc = call(*args, **kwargs) + if rc: + raise CalledProcessError(rc, "Command %s returned non zero exit status" % args[0]) + + def list2cmdline(seq): """ Translate a sequence of arguments into a command line Index: test/test_subprocess.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_subprocess.py,v retrieving revision 1.15 diff -u -r1.15 test_subprocess.py --- test/test_subprocess.py 17 Nov 2004 20:06:35 -0000 1.15 +++ test/test_subprocess.py 23 Nov 2004 13:54:48 -0000 @@ -44,6 +44,23 @@ "import sys; sys.exit(47)"]) self.assertEqual(rc, 47) + def test_xcall_zero(self): + # xcall() function with zero return code + # xcall() just calls call() so we test where the two differ + rc = subprocess.xcall([sys.executable, "-c", + "import sys; sys.exit(0)"]) + self.assertEqual(rc, None) + + def test_xcall_nonzero(self): + # xcall() function with non zero return code + try: + subprocess.xcall([sys.executable, "-c", + "import sys; sys.exit(47)"]) + except subprocess.CalledProcessError, e: + self.assertEqual(e.errno, 47) + else: + self.fail("Expected OSError") + def test_call_kwargs(self): # call() function with keyword args newenv = os.environ.copy()