Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is python's subprocess.call implemented like this?

The subprocess module has the convenience function call, which is implemented like this in both 2.6 and 3.1:

def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()

The documentation for this function carries a red warning, reading:

Warning: Like Popen.wait(), this will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data.

The Popen.wait() documentation says to use Popen.communicate() instead in such circumstances. Well, then why isn't call just implemented like below instead, so the stupid warning can be removed, and silly limitations like this removed from the standard library?

def call(*args, **kwargs):
    input = kwargs.pop("input", None)
    p = Popen(*args, **kwargs)
    p.communicate(input)
    return p.returncode

I'm sure there's a reason. What am I missing?

like image 490
Lauritz V. Thaulow Avatar asked Mar 04 '11 14:03

Lauritz V. Thaulow


People also ask

What does subprocess call do in Python?

The Python subprocess call() function returns the executed code of the program. If there is no program output, the function will return the code that it executed successfully. It may also raise a CalledProcessError exception.

What is the difference between subprocess call and run?

I can say that you use subprocess. call() when you want the program to wait for the process to complete before moving onto the next process. In the case of subprocess. run() , the program will attempt to run all the processes at once, inevitably causing the program to crash.

How do I get the output of a subprocess call?

communicate() #Another way to get output #output = subprocess. Popen(args,stdout = subprocess. PIPE). stdout ber = raw_input("search complete, display results?") print output #... and on to the selection process ...


2 Answers

I spent some time looking through PEP-324, which introduced the subprocess module, trying to figure out the design decisions involved, but I think the answer is actually very simple:

There's no reason to pass stdout=PIPE or stderr=PIPE to subprocess.call, so the fact that it can deadlock is irrelevant.

The only reason to pass stdout=PIPE or stderr=PIPE to subprocess.Popen is so that you can use the Popen instance's stdout and stderr attributes as file objects. Since subprocess.call never lets you see the Popen instance, the PIPE options become irrelevant.

There is potential overhead to Popen.communicate (creating additional threads to avoid deadlock by monitoring the pipes), and there's no benefit in this case, so there's no reason to use it.

Edit: If you want to discard your output, I guess it's better to do so explicitly:

# option 1
with open(os.devnull, 'w') as dev_null:
    subprocess.call(['command'], stdout=dev_null, stderr=dev_null)

# option 2
subprocess.call(['command >& /dev/null'], shell=True)

instead of instructing subprocess to capture all of the output to PIPE files that you never intend to use.

like image 151
Josh Kelley Avatar answered Sep 30 '22 23:09

Josh Kelley


If all you want to do is run a command and get the exit status to determine if it succeeded or failed then you don't need to communicate with it via pipes. That's the convenience of the subprocess.call() method. There are other convenience functions in the subprocess module as well which encapsulate many of the common uses of using the Popen objects in an efficient manner.

If you need to pipe the child processes stdout or stderr somewhere than don't use call(), use a Popen object and communicate() with it just as the docs state.

like image 38
milky414 Avatar answered Oct 01 '22 00:10

milky414