excepciones - ¿Cómo evitar que un bloque de código sea interrumpido por KeyboardInterrupt en Python?
raise python (4)
Coloque la función en un hilo y espere a que el hilo termine.
Los hilos de Python no se pueden interrumpir, excepto con una API especial.
import time
from threading import Thread
def noInterrupt():
for i in xrange(4):
print i
time.sleep(1)
a = Thread(target=noInterrupt)
a.start()
a.join()
print "done"
0
1
2
3
Traceback (most recent call last):
File "C:/Users/Admin/Desktop/test.py", line 11, in <module>
a.join()
File "C:/Python26/lib/threading.py", line 634, in join
self.__block.wait()
File "C:/Python26/lib/threading.py", line 237, in wait
waiter.acquire()
KeyboardInterrupt
¿Ves cómo se aplazó la interrupción hasta que terminó el hilo?
Aquí se adapta a su uso:
import time
from threading import Thread
def noInterrupt(path, obj):
try:
file = open(path, ''w'')
dump(obj, file)
finally:
file.close()
a = Thread(target=noInterrupt, args=(path,obj))
a.start()
a.join()
Estoy escribiendo un programa que almacena algunos resultados a través del módulo pickle. Lo que sucede en este momento es que si pulso ctrl-c mientras se realiza la operación de dump
, el dump
se interrumpe y el archivo resultante se corrompe (es decir, solo se escribe parcialmente, por lo que no se puede load
nuevo).
¿Hay alguna forma de hacer que el dump
, o en general un bloque de código, sea ininterrumpible? Mi solución actual se ve algo como esto:
try:
file = open(path, ''w'')
dump(obj, file)
file.close()
except KeyboardInterrupt:
file.close()
file.open(path,''w'')
dump(obj, file)
file.close()
raise
Parece una tontería reiniciar la operación si se interrumpe, por lo que estoy buscando una forma de diferir la interrupción. ¿Cómo hago esto?
El siguiente es un administrador de contexto que adjunta un controlador de señal para SIGINT
. Si se llama al manejador de señales del administrador de contexto, la señal se retrasa al pasar la señal solo al manejador original cuando el gestor de contexto finaliza.
import signal
import logging
class DelayedKeyboardInterrupt(object):
def __enter__(self):
self.signal_received = False
self.old_handler = signal.signal(signal.SIGINT, self.handler)
def handler(self, sig, frame):
self.signal_received = (sig, frame)
logging.debug(''SIGINT received. Delaying KeyboardInterrupt.'')
def __exit__(self, type, value, traceback):
signal.signal(signal.SIGINT, self.old_handler)
if self.signal_received:
self.old_handler(*self.signal_received)
with DelayedKeyboardInterrupt():
# stuff here will not be interrupted by SIGINT
critical_code()
En mi opinión, el uso de hilos para esto es una exageración. Puede asegurarse de que el archivo se esté guardando correctamente simplemente haciéndolo en un bucle hasta que se realice una escritura exitosa:
def saveToFile(obj, filename):
file = open(filename, ''w'')
cPickle.dump(obj, file)
file.close()
return True
done = False
while not done:
try:
done = saveToFile(obj, ''file'')
except KeyboardInterrupt:
print ''retry''
continue
Use el módulo de signal para deshabilitar SIGINT durante el proceso:
s = signal.signal(signal.SIGINT, signal.SIG_IGN)
do_important_stuff()
signal.signal(signal.SIGINT, s)