1 votes

Difference between subprocess.run and subprocess.Popen

I have a question about the library subprocess :

What is the difference between using subprocess.run() y Popen() and under what circumstances should each be used?

0 votes

Please ask one question per publication, if you have any other question please create another publication. Both questions are independent. Read How to Ask .

7voto

FJSevilla Points 29084

The core of the module subprocess is subprocess.Popen for its part subprocess.run was added in Python 3.5 and is essentially a wrapper/envelope over subprocess.Popen and that was created to integrate and unify several old functions such as subprocess.call . Basically, it allows you to execute a command in a subprocess and wait for until it ends.

The key word is "wait", run block the main process until the command executed in the child process terminates while with subprocess.Popen you can continue in parallel to do things in the parent process in the meantime by calling subprocess.communicate to pass or receive data from the subprocesses when desired.

As mentioned above, subprocess.run only makes it easier to use, underneath it is called a subprocess.Popen and use is made of subprocess.comunicate . In fact, all arguments are passed directly to the constructor of the Popen minus three:

  • timeout : it is passed on to Popen.communicate generating an exception TimeoutExpired if the process does not return before the process. However, in contrast to what happens with the Popen.comunicate raw, prior to throwing the exception, instructions are sent to kill the child process and it waits.

  • input is also passed to Popen.communicate . As a warning, when this argument is used, Popen internally associates stdin with a pipe ( stdin = subprocess.PIPE ) so it should not be used in conjunction with the argument stdin .

  • check if the process exits with a non-zero exit code, an exception will be generated. CalledProcessError .

On the other hand, subproccess.run returns an object subprocess.CompletedProcess representing the result of a completed process:

  • subprocess.CompletedProcess.args are the arguments used to launch the process, in direct relation to the object passed to the argument args from subprocess.run .

  • subprocess.CompletedProcess.returncode is simply the output state of the process. Typically 0 indicates that the process was executed correctly, although logically this is something that is defined by the process itself.

  • subprocess.CompletedProcess.stdout captures the standard output of the subprocess, or None if it has not been captured. It is either a byte string or a text string ( str ) Yes, yes, he did True to the argument universal_newlines=True from subprocess.run . In order to capture the output it is necessary to redirect it through a pipe when calling subprocess.run via stdout=subperocess.PIPE

  • subprocess.CompletedProcess.stderr attribute: exactly the same as the previous attribute, only for stderr .

  • subprocess.CompletedProcess.check_returncode() as when defining check like True in the builder of subprocess.run when called, this method throws an exception CalledProcessError if the return code of the subprocess 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 run and finish before continuing execution in the parent process? . If so, use subprocess.run . Obviously you can achieve the same with subprocess.Popen + comunicate() but requires more code and work (hence the justification for the existence of the run ).

A very simplified example, imagine we have the following script Python on a *nix system that we are going to run as a thread:

import select
import sys
import time

for i in range(10):
    time.sleep(1)
    if select.select([sys.stdin], [], [], 0.0)[0]:
        if sys.stdin.read() == "salir":
            break
print(f"Se ha esperado {i} segundos")

as an example, it does nothing, the process simply takes about 10 seconds to finish but we can stop it at any time if we command it to "exit" via stdin.

If we launch the process via subprocess.run If we do not do this, our parent process blocks for the 10 seconds it takes for the child to return without further ado:

import subprocess

process = subprocess.run(args=["python", "dropdownlist.py"],
                           stdout=subprocess.PIPE,
                           stdin=subprocess.PIPE,
                           encoding='utf8'
                           )
print(process.stdout)

10 seconds waited

Once the subprocess is launched, our main process crashes and only continues (executing print(process.stdout) ) when the subprocess terminates.

On the other hand, using subprocess.Popen we can do other things while the process is running and eventually communicate with it at some point to stop it:

import subprocess
import time

process = subprocess.Popen(args=["python", "dropdownlist.py"],
                           stdout=subprocess.PIPE,
                           stdin=subprocess.PIPE,
                           encoding='utf8'
                           )

time.sleep(5) # hacemos cualquier cosa mientra tanto
out, _ = process.communicate("salir")
print(out)

You have waited 5 seconds

Imagine you have a CLI program that plays audio files and at the same time it returns some information via standard output such as progress and also allows you to send it commands to pause, resume, etc. If you want to run such a program from a script in Python, you could consider two possibilities. If you just want to play from start to finish a file and in the meantime your script Python just waits and does nothing, subprocess.run would be your simplest choice. If you want to be able to pause, resume, etc. playback while using the information the player is sending subprocess.Popen + comunicate (to send the commands and receive the information that the player sends via stdout) is the appropriate option.

Following the same logic, if you want to launch several commands via subprocess destined to run in parallel, you should use subprocess.Popen since subprocess.run would execute them in series.

HolaDevs.com

HolaDevs is an online community of programmers and software lovers.
You can check other people responses or create a new question if you don't find a solution

Powered by:

X