Estoy trabajando en un script de Python que inicia varios procesos y conexiones de bases de datos. De vez en cuando quiero matar el script con una señal Ctrl+ C, y me gustaría hacer una limpieza.
En Perl haría esto:
$SIG{'INT'} = 'exit_gracefully';
sub exit_gracefully {
print "Caught ^C \n";
exit (0);
}
¿Cómo hago el análogo de esto en Python?
./program.py > output.log
. Cuando presiona Ctrl-C , desea que su programa salga correctamente haciendo que registre que todos los archivos de datos se han vaciado y marcado como limpio para confirmar que se han dejado en un buen estado conocido. Pero Ctrl-C envía SIGINT a todos los procesos en una tubería, por lo que el shell puede cerrar STDOUT (ahora "output.log") antes de que program.py termine de imprimir el registro final. Python se quejará, "error de cierre en el destructor de objetos de archivo: Error en sys.excepthook:".Puede tratarlo como una excepción (KeyboardInterrupt), como cualquier otro. Cree un nuevo archivo y ejecútelo desde su shell con los siguientes contenidos para ver a qué me refiero:
fuente
signal.signal(signal.SIGINT, signal.default_int_handler)
o va a fallar, porque KeyboardInterrupt no se dispara en todas las situaciones en las que debería dispararse! Los detalles están aquí .Y como gestor de contexto:
Usar:
Manejadores anidados:
Desde aquí: https://gist.github.com/2907502
fuente
StopIteration
para romper el bucle más interno cuando se presiona un ctrl-C, ¿verdad?Puede manejar CTRL+ Ccapturando la
KeyboardInterrupt
excepción. Puede implementar cualquier código de limpieza en el controlador de excepciones.fuente
De la documentación de Python :
fuente
Otro fragmento
Referido
main
como la función principal yexit_gracefully
como el manejador CTRL+cfuente
Adapte el código de @udi para admitir múltiples señales (nada lujoso):
Este código admite la llamada de interrupción del teclado (
SIGINT
) y elSIGTERM
(kill <process>
)fuente
En contraste con Matt J su respuesta, yo uso un objeto simple. Esto me da la posibilidad de analizar este controlador en todos los subprocesos que deben detenerse.
En otra parte
fuente
time.sleep()
lugar de hacer un ciclo ocupado en una variable.Puede usar las funciones en el módulo de señal incorporado de Python para configurar manejadores de señal en python. Específicamente, la
signal.signal(signalnum, handler)
función se utiliza para registrar lahandler
función para la señalsignalnum
.fuente
gracias por las respuestas existentes, pero agregó
signal.getsignal()
fuente
Si desea asegurarse de que finalice su proceso de limpieza, agregaría a la respuesta de Matt J utilizando un SIG_IGN para que
SIGINT
se ignoren aún más, lo que evitará que se interrumpa su limpieza.fuente
Personalmente, no pude usar try / except KeyboardInterrupt porque estaba usando el modo de socket estándar (IPC) que está bloqueando. Por lo tanto, el SIGINT se activó, pero llegó solo después de recibir datos en el zócalo.
Establecer un controlador de señal se comporta igual.
Por otro lado, esto solo funciona para un terminal real. Otros entornos de inicio podrían no aceptar Ctrl+ Co manejar previamente la señal.
Además, hay "Excepciones" y "BaseExceptions" en Python, que difieren en el sentido de que el intérprete debe salir de forma limpia, por lo que algunas excepciones tienen mayor prioridad que otras (las excepciones se derivan de BaseException)
fuente