Подтвердить что ты не робот

Как предотвратить блокирование блока кода с помощью KeyboardInterrupt в Python?

Я пишу программу, которая кэширует некоторые результаты через модуль pickle. То, что происходит на данный момент, заключается в том, что если я нажму ctrl-c во время выполнения операции dump, dump будет прерван, а результирующий файл будет поврежден (т.е. Только частично написан, поэтому он не может быть load ed снова.

Есть ли способ сделать dump, или вообще блок кода, бесперебойным? Мое текущее обходное решение выглядит примерно так:

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

Кажется глупым перезапустить операцию, если она прерывается, поэтому я ищу способ отложить прерывание. Как это сделать?

4b9b3361

Ответ 1

Поместите функцию в поток и дождитесь завершения потока.

Нити Python не могут быть прерваны, кроме специального C api.

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

Посмотрите, как прерывание было отложено до завершения потока?

Здесь он адаптирован к вашему использованию:

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

Ответ 2

Ниже представлен менеджер контекста, который прикрепляет обработчик сигнала для SIGINT. Если вызывается обработчик сигнала диспетчера контекста, сигнал задерживается, только передавая сигнал исходному обработчику, когда вызывается менеджер контекста.

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

Ответ 3

Используйте модуль signal, чтобы отключить SIGINT в течение всего процесса:

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

Ответ 4

По-моему, использование потоков для этого - перебор. Вы можете убедиться, что файл сохранен правильно, просто выполнив его в цикле до тех пор, пока не будет выполнена успешная запись:

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