64 votes

Comment empêcher un bloc de code d'être interrompu par KeyboardInterrupt en Python?

Je suis en train d'écrire un programme qui met en cache certains résultats via le module pickle. Ce qui se passe actuellement, c'est que si j'appuie sur ctrl-c, tandis que l' dump opération est en cours, dump est interrompu et le fichier est corrompu (c'est à dire que partiellement écrit, il ne peut pas être loaded nouveau.

Est-il un moyen de faire dump, ou en général un bloc de code, sans coupure? Ma solution actuelle ressemble à quelque chose comme ceci:

try:
  file = open(path, 'w')
  dump(obj, file)
  file.close()
except KeyboardInterrupt:
  file.close()
  file.open(path,'w')
  dump(obj, file)
  file.close()
  raise

Il semble idiot de recommencer l'opération si elle est interrompue, donc je suis à la recherche d'une façon de reporter l'interrompre. Comment dois-je faire?

67voto

Gary van der Merwe Points 2913
import signal
import logging

class DelayedKeyboardInterrupt(object):
    def __enter__(self):
        self.signal_received = False
        self.old_handler = signal.getsignal(signal.SIGINT)
        signal.signal(signal.SIGINT, self.handler)

    def handler(self, signal, frame):
        self.signal_received = (signal, 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_recived)

with DelayedKeyboardInterrupt():
    # do stuff here

49voto

Unknown Points 22789

Mettez la fonction dans un thread et attendez la fin du thread.

Les threads Python ne peuvent pas être interrompus sauf avec une API C spéciale.

 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
 

Vous voyez comment l'interruption a été différée jusqu'à la fin du thread?

Ici, il est adapté à votre utilisation:

 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()
 

31voto

Utilisez le module de signal pour désactiver SIGINT pendant la durée du processus:

 s = signal.signal(signal.SIGINT, signal.SIG_IGN)
do_important_stuff()
signal.signal(signal.SIGINT, s)
 

11voto

Nadia Alramli Points 40381

À mon avis, utiliser des threads pour cela est une exagération. Vous pouvez vous assurer que le fichier est enregistré correctement en le faisant simplement en boucle jusqu'à ce qu'une écriture réussie soit effectuée:

 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
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X