Captura / locking de SIGINT durante una llamada al sistema

He escrito un rastreador web que me gustaría poder detener a través del teclado. No quiero que el progtwig muera cuando lo interrumpa; primero necesita descargar sus datos al disco. Tampoco quiero capturar KeyboardInterruptedException , porque los datos persistentes podrían estar en un estado incoherente.

Mi solución actual es definir un manejador de señales que capture SIGINT y establezca una bandera; cada iteración del bucle principal comprueba esta bandera antes de procesar la siguiente url.

Sin embargo, he encontrado que si el sistema está ejecutando socket.recv() cuando envío la interrupción, obtengo esto:

 ^C Interrupted; stopping... // indicates my interrupt handler ran Traceback (most recent call last): File "crawler_test.py", line 154, in  main() ... File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 397, in readline data = recv(1) socket.error: [Errno 4] Interrupted system call 

y el proceso sale completamente. ¿Por qué pasó esto? ¿Hay alguna manera de evitar que la interrupción afecte la llamada al sistema?

socket.recv() llama a la función recv compatible con POSIX subyacente en la capa C, que, a su vez, devolverá un código de error EINTR cuando el proceso recibe un SIGINT mientras espera los datos entrantes en recv() . Este código de error se puede usar en el lado C (si estaba progtwigndo en C) para detectar que se devolvió recv() no porque haya más datos disponibles en el zócalo, sino porque el proceso recibió un SIGINT . De todos modos, este código de error se convierte en una excepción en Python, y como nunca se captura, finaliza su aplicación con el rastreo que ve. La solución es simplemente capturar socket.error , verificar el código de error y, si es igual a errno.EINTR , ignorar la excepción silenciosamente. Algo como esto:

 import errno try: # do something result = conn.recv(bufsize) except socket.error as (code, msg): if code != errno.EINTR: raise 

Si no desea que se interrumpa su llamada de socket, desactive el comportamiento de interrupción después de configurar el controlador de señal.

 signal.signal(, ) signal.siginterrupt(, False) 

En la función de manejo de señales, establezca alguna bandera, por ejemplo, un threading.Event () y luego verifique esa bandera en su función de procesamiento principal y finalice su rastreador con gracia.

Información de fondo aquí:

  • Página del manual de linux signal Ver la discusión sobre el indicador SA_RESTART.
  • documentos de python