Curso Python DGA 2011/sistemas/procesos

Procesos en Python
En esta sección veremos cómo usar el módulo subprocess para poder lanzar procesos desde python, y conectar con sus entradas y salidas estándar y de error, así como obtener sus valores de retorno.

Unifica los módulos y funciones existentes para el control de procesos. Ventajas y motivaciones del módulo:
 * Reemplazar y unificar los variados módulos y funciones existentes, tales como os.system, os.spawn*, os.popen*, popen2.*, y commands.*.
 * Excepciones inter-proceso: Si se produce una excepción en el proceso hijo antes de ejecutar el comando, esta excepción se relanza al proceso padre. Ej: OSError cuando se intenta ejecutar un fichero que no existe
 * Posibilidad de ejecutar un hook entre fork y exec
 * No se llama implícitamente a la shell
 * Único interfaz para todas las posibles combinaciones de redirección de stdin, stdout, y stderr (en lugar de tener que usar multiples funciones, como popen2, popen3, etc
 * Soporte para conectar varios subprocesos (shell pipe)
 * Método comunicate para facilitar el envío de datos por el stdin y leer de stdout y stderr con seguridad de no caer en deadlocks.

subprocess define la clase 'Popen':

Argumentos y ejecución en shell o fuera de ella

 * args  debe ser una string describiendo el comando a ejecutar, o una lista de cadenas empezando por el comando y seguido por los parámetros a ejecutar, segun el caso:


 * En unix:
 * Si 'shell=False' (por defecto), args ha de ser una lista. Si se pasa una string, ha de ser únicamente el path al comando a ejecutar
 * Si 'shell=True', args ha de ser una cadena que será pasada tal cual a una shell.


 * Atención: Siempre que la ejecución de un comando vaya a hacerse como resultado de un input por parte de un usuario, ha de evitarse la ejecución en shell, puesto que es vulnerable a inyección de comandos.

Parámetros stdin, stdout y stderr
Sirven para poder redireccionar las entradas y salidas estandar y de error del nuevo proceso.
 * Estos parámetros admiten:
 * Un descriptor de archivo (entero positivo)
 * Un objeto de tipo file
 * subprocess.PIPE: Si se especifica esto, una nueva tubería o "pipe" será creada para conectar con el nuevo proceso.
 * None: No habrá ningún tipo de redirección

Si se usa PIPE, los objetos tipo file que podemos usar para escribir se encuentran en Popen.stdin,Popen.stdout, y Popen.stderr''. Atención: Para evitar deadlocks provocados por el bloqueo del proceso hijo por llenado de los buffers de las pipes, usar la función communicate en lugar de las pipes directamente.

Otros parámetros

 * bufsize: Si se pasa, mismo significado que la función built-in open de python: 0 - no se usa buffer. 1 - buffer por línea, y cualquier otro número positivo significa que se usará un buffer de ese tamaño. Un número negativo indica que se usará el valor por defecto del sistema operativo.
 * preexec_fn: Función que se ejecutará cuando se cree el proceso hijo, antes de la ejecución del programa o comando pasado (Solo en unix).
 * cwd: Path al que se cambiará el directorio actual de trabajo antes de ejecutar el proceso.
 * env: Map para sobreescribir las variables de entorno del sistema.

Obteniendo el código de retorno y el pid del proceso

 * El código de retorno se obtiene a través de Popen.returncode
 * El pid del nuevo proceso creado a través de Popen.pid

Otras funciones de subprocess para simplificar el uso

 * subprocess.call(*popenargs, **kwargs): Ejecuta el comando especificado en popenargs (mismo uso que en Popen), espera a la finalización del comando y devuelve el código de error. Se pueden pasar los mismos parámetros con nombre que en Popen.
 * subprocess.check_call(*popenargs, **kwargs): Igual que la anterior, pero lana una CalledProcessError si el valor de retorno es menor que 0. El objeto CalledProcessError contiene el código de retorno en el atributo returncode.
 * subprocess.check_output(*popenargs, **kwargs): Igual que la anterior, pero devuelve la salida como una cadena.

Esperando a que acaben los procesos y comprobando estado

 * El método wait de Popen bloquea hasta que el proceso acaba.
 * El método poll' de Popen devuelve el código de retorno si el proceso ha acabado, o None si está en curso.

Terminando procesos

 * El método terminate de Popen envía SIGTERM al proceso hijo en Unix. En Windows, ejecuta TerminateProcess del api de win32.
 * El método kill de Popen envíoa SIGKILL al proceso hijo en Unix. En Windows es un alias para terminate.

Enviando señales a procesos

 * Se pueden enviar señales a un proceso usando el método send_signal(signal).
 * También se puede usar el método kill(pid, signal) del módulo 'os'

Deadlocks y el método communicate
El uso de stdin y stdout/stderr mediante pipes en los procesos puede causar deadlocks si el proceso hijo genera suficientes datos en la pipe de salida tal que se bloquea esperando que el buffer acepte más datos. Para evitarlo, puede usarse Popen.communicate(input=None)


 * Interacciona con el proceso, envía datos al stdin y lee del stdout y stderr
 * Espera a que acabe el proceso
 * Se le puede enviar datos al proceso usando el parámetro input como una string
 * Devuelve una tupla (stdoutdata, stderrdata)
 * Si se quiere poder enviar al stdin y recibir del stdout y stderr, se ha de pasar PIPE al crear los objetos Popen.