Sub-procesos¶
Código fuente: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py
Esta sección describe APIs de alto nivel de async/await de asyncio para crear y gestionar sub-procesos.
Aquí podemos encontrar un ejemplo de cómo asyncio puede ejecutar un comando de shell y obtener su resultado:
import asyncio
async def run(cmd):
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
print(f'[{cmd!r} exited with {proc.returncode}]')
if stdout:
print(f'[stdout]\n{stdout.decode()}')
if stderr:
print(f'[stderr]\n{stderr.decode()}')
asyncio.run(run('ls /zzz'))
mostrará en pantalla:
['ls /zzz' exited with 1]
[stderr]
ls: /zzz: No such file or directory
Ya que todos las funciones de sub-procesos de asyncio son asíncronas y asyncio proporciona herramientas para trabajar con tales funciones, es fácil ejecutar y monitorear múltiples subprocesos en paralelo. De hecho es trivial modificar los ejemplos de arriba para ejecutar varios comandos simultáneamente:
async def main():
await asyncio.gather(
run('ls /zzz'),
run('sleep 1; echo "hello"'))
asyncio.run(main())
Véase también la subsección Examples.
Creando sub-procesos¶
-
coroutine
asyncio.
create_subprocess_exec
(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)¶ Crea un sub-proceso.
El argumento limit establece el límite del buffer para los envoltorios
StreamReader
paraProcess.stdout
yProcess.stderr
(si se pasasubprocess.PIPE
a los argumentos stdout y stderr).Retorna una instancia de
Process
.Véase la documentación de
loop.subprocess_exec()
para los otros parámetros.Distinto en la versión 3.10: Removed the loop parameter.
-
coroutine
asyncio.
create_subprocess_shell
(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)¶ Ejecuta el comando de shell cmd.
El argumento limit establece el límite del buffer para los envoltorios
StreamReader
paraProcess.stdout
yProcess.stderr
(si se pasasubprocess.PIPE
a los argumentos stdout y stderr).Retorna una instancia de
Process
.Véase la documentación de
loop.subprocess_shell()
para los otros parámetros.Importante
Es la responsabilidad de la aplicación asegurarse que todos los espacios en blanco y caracteres especiales estén citados apropiadamente para evitar vulnerabilidades de inyección de instrucciones. La función
shlex.quote()
puede ser usada para escapar caracteres en blanco y caracteres especiales de shell en cadenas de caracteres a ser usadas para construir comandos de shell.Distinto en la versión 3.10: Removed the loop parameter.
Nota
Los subprocesos están disponibles para Windows si se utiliza un ProactorEventLoop
. Consulte Soporte de subprocesos en Windows para obtener más detalles.
Ver también
asyncio también tiene las siguientes APIs de bajo nivel para trabajar con sub-procesos: loop.subprocess_exec()
, loop.subprocess_shell()
, loop.connect_read_pipe()
, loop.connect_write_pipe()
, así como también los Sub-procesos de Transportes y los Sub-procesos de Protocolos.
Constantes¶
-
asyncio.subprocess.
PIPE
¶ Se le puede pasar a los parámetros stding, stdout o stderr.
Si se le pasa PIPE al argumento stdin, el atributo
Process.stdin
apuntará a la instanciaStreamWriter
.Si se pasa PIPE a los argumentos stdout o stderr, los atributos
Process.stdout
yProcess.stderr
apuntarán a las instanciasStreamReader
.
-
asyncio.subprocess.
STDOUT
¶ Un valor especial que puede ser usado como el argumento de stderr e indica que los errores estándar deben ser redireccionados en la salida estándar.
-
asyncio.subprocess.
DEVNULL
¶ Un valor especial que puede ser usado como el argumento de stdin, stdout, stderr para procesar funciones de creación. Indica que el archivo especial
os.devnull
será usado para la correspondiente transmisión del sub-proceso.
Interactuando con Subprocesos¶
Las dos funciones create_subprocess_exec()
y create_subprocess_shell()
retornan instancias de la clase Process. Process es un envoltorio de alto nivel que permite comunicarse con los subprocesos y estar atento a sus términos.
-
class
asyncio.subprocess.
Process
¶ Un objeto que envuelve procesos del SO creados por las funciones
create_subprocess_exec()
ycreate_subprocess_shell()
.Esta clase está diseñada para tener un API similar a la clase
subprocess.Popen
, pero hay algunas diferencias notables:a diferencia de Popen, las instancias de Process no tiene un equivalente del método
poll()
;the
communicate()
andwait()
methods don’t have a timeout parameter: use thewait_for()
function;el método
Process.wait()
es asíncrono, mientras que el métodosubprocess.Popen.wait()
es implementado como un bucle de espera activa;el parámetro universal_newlines no es compatible.
Esta clase no es segura para subprocesos (not thread safe).
Véase también la sección Subprocesos e Hilos.
-
coroutine
wait
()¶ Espera a que el proceso hijo termine.
Define y retorna el atributo
returncode
.Nota
Este método puede bloquearse cuando usa
stdout=PIPE
ostderr=PIPE
y el proceso hijo genera tanta salida que se bloquea esperando a que la tubería de búfer del SO acepte más datos. Use el métodocommunicate()
cuando use tuberías para evitar esta condición.
-
coroutine
communicate
(input=None)¶ Interactuar con el proceso:
envía datos a stdin (si input no es
None
);lee datos desde stdout y stderr, hasta que el llegue al EOF;
espera a que el proceso termine.
El argumento opcional input son los datos (objetos
bytes
) que serán enviados a los procesos hijos.Retorna una tupla
(stdout_data, stderr_data)
.Si se lanza la excepción
BrokenPipeError
oConnectionResetError
cuando se escribe input en stdin, la excepción es ignorada. Esta condición ocurre cuando el proceso finaliza antes de que todos los datos sean escritos en stdin.Si se desea enviar datos al stdin del proceso, el proceso necesita ser creado con
stdin=PIPE
. De manera similar, para obtener cualquier cosa exceptoNone
en la tupla del resultado, el proceso tiene que ser creado con los argumentosstdout=PIPE
y/ostderr=PIPE
.Tenga en cuenta que los datos leídos son almacenados en memoria, por lo que no utilice este método si el tamaño de los datos es más grande o ilimitado.
-
send_signal
(signal)¶ Envíe la señal signal al proceso hijo.
Nota
En Windows,
SIGTERM
es un alias paraterminate()
. Se puede enviarCTRL_C_EVENT
yCTRL_BREAK_EVENT
a procesos iniciados con un parámetro creationflags que incluyeCREATE_NEW_PROCESS_GROUP
.
-
terminate
()¶ Para al proceso hijo.
En sistemas POSIX este método envía
signal.SIGNTERM
al proceso hijo.En Windows, la función de la API de Win32
TerminateProcess()
es llamado para parar a los procesos hijos.
-
kill
()¶ Mata el proceso hijo.
En sistemas POSIX, este método envía
SIGKILL
al proceso hijo.En Windows este método es un alias para
terminate()
.
-
stdin
¶ Flujo de entrada estándar (
StreamWriter
) oNone
si el proceso fue creado constdin=None
.
-
stdout
¶ Flujo de salida estándar (
SreamReader
) oNone
si el proceso fue creado constdout=None
.
-
stderr
¶ Flujo de salida estándar (
StreamReader
) oNone
si el proceso fue creado constderr=None
.
Advertencia
Use the
communicate()
method rather thanprocess.stdin.write()
,await process.stdout.read()
orawait process.stderr.read()
. This avoids deadlocks due to streams pausing reading or writing and blocking the child process.-
pid
¶ Número de identificación del Proceso (PID por sus siglas en inglés).
Tenga en cuenta que para procesos creados por la función
create_subprocess_shell()
, este atributo es el PID del shell generado.
-
returncode
¶ Código de retorno del proceso cuando finaliza.
Un valor
None
indica que el proceso todavía no ha finalizado.Un valor negativo
-N
indica que el hijo ha finalizado por la señalN
(sólo para POSIX).
Subprocesos y Hilos¶
Por defecto el bucle de eventos de asyncio estándar permite correr subprocesos desde hilos diferentes.
En Windows, sólo ProactorEventLoop
proporciona subprocesos (por defecto), SelectorEventLoop
no es compatible con subprocesos.
En UNIX, los observadores de hijos son usados para la espera de finalización de subprocesos, véase Observadores de procesos para más información.
Distinto en la versión 3.8: UNIX cambió para usar ThreadedChildWatcher
para generar subprocesos de hilos diferentes sin ninguna limitación.
Crear un subproceso con el observador del hijo inactivo lanza un RuntimeError
.
Tenga en cuenta que implementaciones alternativas del bucle de eventos pueden tener limitaciones propias; por favor consulte su documentación.
Ver también
La sección Concurrencia y multihilos en asyncio.
Ejemplos¶
Un ejemplo usando la clase Process
para controlar un subproceso y la clase StreamReader
para leer de su salida estándar.
El subproceso es creado por la función create_subprocess_exec()
:
import asyncio
import sys
async def get_date():
code = 'import datetime; print(datetime.datetime.now())'
# Create the subprocess; redirect the standard output
# into a pipe.
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', code,
stdout=asyncio.subprocess.PIPE)
# Read one line of output.
data = await proc.stdout.readline()
line = data.decode('ascii').rstrip()
# Wait for the subprocess exit.
await proc.wait()
return line
date = asyncio.run(get_date())
print(f"Current date: {date}")
Véase también los ejemplos de prueba escritos usando APIs de bajo nivel.