15

If I invoke a process with subprocess.Popen in Python as follows:

myproc = subprocess.Popen(...).communicate()

what is the correct way to see its status? Not its output to stdout or stderr, but its exit status once it's finished (e.g. 0 for success or another for failure)?

3 Answers3

32

returncode is indeed the answer, but the solution doesn't need to be complicated.

process = subprocess.Popen(...)
stdoutdata, stderrdata = process.communicate()
print process.returncode

More info in the Python subprocess docs.

  • is ``wait`` really required or does ``communicate()`` take of things when the process is still running? –  Dec 26 '12 at 16:27
  • 6
    @user248237 The docs say "return code, set by `poll()` and `wait()` (and indirectly by `communicate()`)." So `communicate()` should take care of waiting for the process to end. –  Dec 26 '12 at 16:29
10

A process doesn't have a return code until it's finished executing. Therefore, if it hasn't yet finished, you have to decide what you want to do: wait for it, or return some indicator of "I haven't finished yet".

If you want to wait, use communicate and then check the returncode attribute.

If you want to check whether the return code is set, and return None if not, use Popen.poll().

Popen.poll()

Check if child process has terminated. Set and return returncode attribute.

(if process hasn't terminated, poll() returns None)

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Katriel
  • 120,462
  • 19
  • 136
  • 170
1

You may need to call a wait on your subprocess and then (once is done) check the status in the returncode field of the subprocess instance.

I have a little routine that calls stuff, maybe it'll help...

def singleProcessExecuter(command, ** kwargs):
    assert isinstance(command, list), "Expected 'command' parameter to be a list containing the process/arguments to execute. Got %s of type %s instead" % (command, type(command))
    assert len(command) > 0, "Received empty list of parameters"
    retval = {
            "exitCode": -1,
            "stderr": u"",
            "stdout": u"",
            "execTime": datetime.timedelta(0),
            "command": None,
            "pid": None
            }
    retval["command"] = command
    log.info("::singleProcessExecuter > At %s, executing \"%s\"" % (datetime.datetime.now(), " ".join(command)))
    #print("::singleProcessExecuter > At %s, executing \"%s\"" % (datetime.datetime.now(), " ".join(parameter)))
    cwd = kwargs.get("cwd", os.getcwd())
    user = kwargs.get("user", getUid())
    sheel = kwargs.get("shell", False)
    startDatetime = datetime.datetime.now()
    myPopy = subprocess.Popen(command, cwd=cwd, preexec_fn=os.seteuid(getUid(user)), shell=sheel, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
    retval["pid"] = myPopy.pid
    log.debug("::singleProcessExecuter > Command \"%s\" got pid %s" % (" ".join(command), myPopy.pid))
    try:
        retval["stdout"], retval["stderr"] = myPopy.communicate()
        myPopy.wait()
    except OSError, osErr:
        log.debug("::singleProcessExecuter > Got %s %s in myPopy.communicate() when trying get output of command %s. It is probably a bug (more info: http://bugs.python.org/issue1731717)" % (osErr, type(osErr), command[0]))
    except Exception, e:
        log.warn("::singleProcessExecuter > Got %s %s when trying to get stdout/stderr outputs of %s" % (type(e), e, " ".join(command)))
        log.debug("::singleProcessExecuter > Got %s %s when trying to get stdout/stderr outputs of %s. Showing traceback:\n%s" % (type(e), e, " ".join(command), traceback.format_exc()))
        raise
    retval["exitCode"] = myPopy.returncode
    retval["execTime"] = datetime.datetime.now() - startDatetime
    #print(":singleProcessExecuter > This is %s's retval:\n%s" % (" ".join(parameter), retval)) 
    return retval

You can try it with:

print "This is the return: %s" % singleProcessExecuter(["ls", "-la"])
Savir
  • 17,568
  • 15
  • 82
  • 136
  • I really like your code! I know it's been a few years, but I'd be glad to see the needed imports for that! – schneiti May 08 '15 at 13:40