Python subprocess .check_call vs .check_output

check_call() returns as soon as /bin/sh process exits without waiting for descendant processes (assuming shell=True as in your case).

check_output() waits until all output is read. If ssh inherits the pipe then check_output() will wait until it exits (until it closes its inherited pipe ends).

check_call() code example:

#!/usr/bin/env python
import subprocess
import sys
import time

start = time.time()
cmd = sys.executable + " -c 'import time; time.sleep(2)' &"
subprocess.check_call(cmd, shell=True)
assert (time.time() - start) < 1

The output is not read; check_call() returns immediately without waiting for the grandchild background python process.

check_call() is just Popen().wait(). Popen() starts the external process and returns immediately without waiting for it to exit. .wait() collects the exit status for the process — it doesn’t wait for other (grandchildren) processes.

If the output is read (it is redirected and the grandchild python
process inherits the stdout pipe):

start = time.time()
subprocess.check_output(cmd, shell=True)
assert (time.time() - start) > 2

then it waits until the background python process that inherited the pipe exits.

check_output() calls Popen().communicate(), to get the output. .communicate() calls .wait() internally i.e., check_output() also waits for the shell to exit and check_output() waits for EOF.

If the grandchild doesn’t inherit the pipe then check_output() doesn’t wait for it:

start = time.time()
cmd = sys.executable + " -c 'import time; time.sleep(2)' >/dev/null &"
subprocess.check_output(cmd, shell=True)
assert (time.time() - start) < 1

Grandchild’s output is redirected to /dev/null i.e., it doesn’t inherit the parent’s pipe and therefore check_output() may exit without waiting for it.

Note: & at the end which puts the grandchild python process into background. It won’t work on Windows where shell=True starts cmd.exe by default.

Leave a Comment