I have a question about the library subprocess
:
What is the difference between using subprocess.run()
and Popen()
and in what circumstances should each be used?
I have a question about the library subprocess
:
What is the difference between using subprocess.run()
and Popen()
and in what circumstances should each be used?
The core of the module
subprocess
issubprocess.Popen
, for its part itsubprocess.run
was added in Python 3.5 and is basically a wrapper oversubprocess.Popen
and that was created to integrate and unify various old functions likesubprocess.call
. It basically allows you to run a command on a thread and wait until it finishes.The keyword is "wait", it
run
blocks the main process until the command executed in the child process finishes while withsubprocess.Popen
you can continue doing things in the parent process in parallel in the meantime, calling tosubprocess.communicate
pass or receive data from the threads whenever you want .As already mentioned,
subprocess.run
it only makes it easier to use, below it callssubprocess.Popen
and makes use ofsubprocess.comunicate
. In fact, all arguments are passed directly to thePopen
minus-three constructor:timeout
: it is passed toPopen.communicate
generating an exceptionTimeoutExpired
if the process does not return before it. However, unlike what happens withPopen.comunicate
raw, before throwing the exception instructions are sent to kill the child process and wait.input
: is also passed toPopen.communicate
. As a caveat, when this argument is used internally Popen associates stdin with a pipe (stdin = subprocess.PIPE
) so it should not be used in conjunction with the argumentstdin
.check
: If the process exits with a non-zero exit code, an exception will be thrownCalledProcessError
.On the other hand,
subproccess.run
it returns an objectsubprocess.CompletedProcess
representing the result of a terminated process:subprocess.CompletedProcess.args
: are the arguments used to launch the process, in direct relation to the object passed to the argumentargs
ofsubprocess.run
.subprocess.CompletedProcess.returncode
: is simply the exit status of the process. Typically 0 indicates that the process was executed correctly, although logically it is something that is defined by the process itself.subprocess.CompletedProcess.stdout
: captures the standard output of the thread orNone
if it has not been captured. It is a byte string or a text string (str
) if it was passedTrue
to the argumentuniversal_newlines=True
ofsubprocess.run
. In order to capture the output it is necessary to redirect it via a pipe by callingsubprocess.run
viastdout=subperocess.PIPE
subprocess.CompletedProcess.stderr
: exactly the same as the previous attribute, only forstderr
.subprocess.CompletedProcess.check_returncode()
: Just as when definedcheck
asTrue
in the constructor ofsubprocess.run
, this method when called throws an exceptionCalledProcessError
if the thread's return code is not zero.At this point, the second question you raise about when to use one or the other is already answered. What you have to ask yourself is do I want to wait for the command to execute and finish before continuing execution in the parent process? . If so, use
subprocess.run
. You can obviously achieve the same thing withsubprocess.Popen
+comunicate()
, but it requires more code and work (hence the rationale forrun
).For a very simplified example, let's imagine we have the following Python script on a *nix system that we're going to run as a thread:
as an example, it doesn't do anything, it just takes about 10 seconds for the process to finish but we can stop it at any time if we send it "exit" via stdin.
If we launch the process via subprocess.run , our parent process is blocked for the 10 seconds that the child takes to return without further ado:
Once the thread is launched, our main process blocks and only continues (executing
print(process.stdout)
) when the thread terminates.Instead using
subprocess.Popen
we can do other things while the process is running and eventually communicate with it at some point to stop it:Imagine that you have a CLI program that plays audio files and at the same time that it plays, it returns certain information via standard output such as the progress and also allows you to send commands to pause, resume, etc. If you want to run such a program from a Python script, you could consider two possibilities. If you just want to play a file from start to finish and meanwhile your Python script just waits and does nothing, that
subprocess.run
would be your simplest choice. If you want to be able to stop, resume, etc. the playback while using the information that the player sendssubprocess.Popen
+comunicate
(to send the commands and receive the information that the player sends via stdout) is the right option.Following the same logic, if you want to issue several threaded commands intended to run in parallel, you should use
subprocess.Popen
since it wouldsubprocess.run
execute them serially.