Как предотвратить прерывание блока кода с помощью KeyboardInterrupt в Python? - PullRequest
47 голосов
/ 09 мая 2009

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

Есть ли способ сделать 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

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

Ответы [ 5 ]

59 голосов
/ 21 февраля 2014

Ниже приведен контекстный менеджер, который подключает обработчик сигнала для 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()
37 голосов
/ 09 мая 2009

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

Потоки Python не могут быть прерваны, кроме как с помощью специального API C.

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()
25 голосов
/ 09 мая 2009

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

s = signal.signal(signal.SIGINT, signal.SIG_IGN)
do_important_stuff()
signal.signal(signal.SIGINT, s)
10 голосов
/ 09 мая 2009

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

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
1 голос
/ 20 марта 2019

Этот вопрос касается блокировки KeyboardInterrupt, но в этой ситуации я считаю, что запись атомарного файла более чистая и обеспечивает дополнительную защиту.

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

После запуска pip install atomicwrites просто используйте его так:

from atomicwrites import atomic_write

with atomic_write(path, overwrite=True) as file:
    dump(obj, file)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...