Python Popen – esperar vs comunicarse vs CalledProcessError

Continuando con mi pregunta anterior , veo que para obtener el código de error de un proceso que generé a través de Popen en Python, tengo que llamar a wait () o communic () (que se puede usar para acceder a los atributos stdout y stderr de Popen):

app7z = '/path/to/7z.exe' command = [app7z, 'a', dstFile.temp, "-y", "-r", os.path.join(src.Dir, '*')] process = Popen(command, stdout=PIPE, startupinfo=startupinfo) out = process.stdout regCompressMatch = re.compile('Compressing\s+(.+)').match regErrMatch = re.compile('Error: (.*)').match errorLine = [] for line in out: if len(errorLine) or regErrMatch(line): errorLine.append(line) if regCompressMatch(line): # update a progress bar result = process.wait() # HERE if result: # in the hopes that 7z returns 0 for correct execution dstFile.temp.remove() raise StateError(_("%s: Compression failed:\n%s") % (dstFile.s, "\n".join(errorLine))) 

Sin embargo, los documentos advierten que wait() puede interrumpirse (cuando stdout = PIPE, que es el caso aquí), mientras que se communicate() . Asi que:

  1. ¿Qué es lo apropiado para usar aquí? Tenga en cuenta que yo uso la salida
  2. ¿Cómo exactamente debo usar comunicar? Podría ser:

     process = Popen(command, stdout=PIPE, startupinfo=startupinfo) out = process.communicate()[0] # same as before... result = process.returncode if result: # ... 

    No estoy seguro sobre el locking y los errores de memoria.

  3. ¿Alguna forma mejor / más python de manejar el problema? No creo que subprocess.CalledProcessError o subprocess.check_call/check_output apliquen en mi caso, ¿o no?

DESCARGO DE RESPONSABILIDAD: No escribí el código, soy el mantenedor actual, por lo tanto, la pregunta 3.

Relacionado:

  • Comando popen de Python. Espera hasta que el comando termine
  • Verifique el código de retorno de un comando cuando el subproceso genera una excepción CalledProcessError
  • esperar proceso hasta que todos los subprocesos terminen?

Estoy en Windows si esto hace una diferencia – Python 2.7.8

Debería haber una, y preferiblemente solo una, obvia forma de hacerlo.

  • sobre el interlocking: es seguro usar stdout=PIPE y wait() juntos si leen desde la tubería. .communicate() hace la lectura y llama a wait() para usted
  • sobre la memoria: si la salida puede ser ilimitada, entonces no debe usar .communicate() que acumula toda la salida en la memoria.

¿Qué es lo apropiado para usar aquí?

Para iniciar el subproceso, lea su salida línea por línea y espere a que salga:

 #!/usr/bin/env python from subprocess import Popen, PIPE process = Popen(command, stdout=PIPE, bufsize=1) with process.stdout: for line in iter(process.stdout.readline, b''): handle(line) returncode = process.wait() 

Este código no se bloquea debido a un búfer de tubería de sistema operativo finito. Además, el código admite comandos con salida ilimitada (si una línea individual cabe en la memoria).

iter() se usa para leer una línea tan pronto como se vacía el búfer stdout del subproceso, para solucionar el error de lectura anticipada en Python 2 . Podría usar una for line in process.stdout simple for line in process.stdout De lo contrario, si no necesita leer las líneas tan pronto como se escriben sin esperar a que se llene el búfer o que finalice el proceso secundario. Consulte Python: lea la entrada de transmisión desde subprocess.communicate () .

Si sabe que la salida del comando puede caber en la memoria en todos los casos, puede obtener la salida de una sola vez:

 #!/usr/bin/env python from subprocess import check_output all_output = check_output(command) 

CalledProcessError si el comando vuelve con un estado de salida distinto de cero. Internamente, check_output() usa Popen() y .communicate()

Debería haber una, y preferiblemente solo una, obvia forma de hacerlo.

subprocess.Popen() es la API principal que funciona en muchos casos. Hay funciones / métodos de conveniencia como Popen.communicate() , check_output() , check_call() para casos de uso comunes.

Existen múltiples métodos, funciones porque hay múltiples casos de uso diferentes.