¿Qué diferencia entre subprocess.call () y subprocess.Popen () hace que PIPE sea menos seguro para el primero?

He echado un vistazo a la documentación de ambos.

Esta pregunta es provocada por el comentario de JF aquí: Recuperar la salida de subprocess.call ()

La documentación actual de Python para subprocess.call() dice lo siguiente sobre el uso de PIPE para subprocess.call() :

Nota No utilice stdout=PIPE o stderr=PIPE con esta función. El proceso hijo se bloqueará si genera suficiente salida a una tubería para llenar el búfer de tubería del sistema operativo ya que no se están leyendo las tuberías.

Python 2.7 subprocess.call() :

Nota No utilice stdout=PIPE o stderr=PIPE con esta función, ya que puede interrumpirse en función del volumen de salida del proceso secundario. Utilice Popen con el método de comunicación () cuando necesite tuberías.

Python 2.6 no incluye tales advertencias.

Además, el subprocess.call() y subprocess.check_call() no parecen tener una forma de acceder a su salida, excepto para usar stdout = PIPE con se comunica ():

https://docs.python.org/2.6/library/subprocess.html#convenience-functions

Tenga en cuenta que si desea enviar datos a la stdin del proceso, debe crear el objeto Popen con stdin=PIPE . De manera similar, para obtener algo que no sea Ninguno en la tupla de resultados, también debe proporcionar stdout=PIPE y / o stderr=PIPE .

https://docs.python.org/2.6/library/subprocess.html#subprocess.Popen.communicate

¿Qué diferencia entre subprocess.call() y subprocess.Popen() hace que PIPE sea ​​menos seguro para subprocess.call() ?

Más específico: ¿Por qué subprocess.call() “interlocking basado en el volumen de salida del proceso hijo”. , y no Popen() ?

call() es solo Popen().wait() (± manejo de errores) .

No debe usar stdout=PIPE con call() porque no se lee de la tubería y, por lo tanto, el proceso hijo se bloqueará tan pronto como llene el búfer de tubería del sistema operativo correspondiente. Aquí hay una imagen que muestra cómo fluyen los datos en command1 | command2 command1 | command2 shell pipeline:

buffers pipe / stdio

No importa cuál sea su versión de Python: el buffer de tubería (ver la imagen) está fuera de su proceso de Python. Python 3 no usa C stdio pero afecta solo el búfer interno. Cuando se vacía el búfer interno, los datos entran en la tubería. Si el command2 (su progtwig Python primario) no se lee desde la canalización, el command1 (el proceso secundario, por ejemplo, iniciado por call() ) se bloqueará tan pronto como el buffer del tubo esté lleno ( pipe_size = fcntl(p.stdout, F_GETPIPE_SZ) ~ 65K en mi caja de Linux (el valor máximo es /proc/sys/fs/pipe-max-size ~ 1M)).

Puede usar stdout=PIPE si lee el Popen.communicate() más adelante, por ejemplo, utilizando el método Popen.communicate() . También puede leer directamente desde process.stdout (el objeto de archivo que representa la canalización) .

Tanto call como Popen proporcionan medios para acceder a la salida de su comando:

  • Con Popen , puede usar communicate o proporcionar un descriptor de archivo u objeto de archivo al parámetro stdout=...
  • Con call su única opción es pasar un descriptor de archivo o un objeto de archivo al parámetro stdout=... (no puede usar communicate con este).

Ahora, la razón por la cual stdout=PIPE es inseguro cuando se usa con call es porque call no regresa hasta que el subproceso haya finalizado, esto significa que toda la salida tendrá que residir en la memoria hasta ese momento, y si la cantidad de salida es demasiado Mucho entonces eso llenaría el buffer de las tuberías del sistema operativo.

Las referencias donde puede validar la información anterior son las siguientes:

  1. De acuerdo con esto, los parámetros tanto para call como para Popen son los mismos:

Los argumentos que se muestran arriba son simplemente los más comunes, que se describen a continuación en Argumentos de uso frecuente (de ahí la notación ligeramente extraña en la firma abreviada). La firma de la función completa es la misma que la del constructor de Popen: esta función pasa todos los argumentos proporcionados directamente a esa interfaz.

  1. De acuerdo con esto, los valores posibles para el parámetro stdout son:

Los valores válidos son PIPE, un descriptor de archivo existente (un entero positivo), un objeto de archivo existente y Ninguno. PIPE indica que se debe crear una nueva canalización para el niño