I'm writing a program that caches some results via the pickle module. What happens at the moment is that if I hit ctrl-c at while the dump
operation is occurring, dump
gets interrupted and the resulting file is corrupted (i.e. only partially written, so it cannot be load
ed again.
Is there a way to make dump
, or in general a block of code, uninterruptable? My current workaround looks something like this:
try: file = open(path, 'w') dump(obj, file) file.close() except KeyboardInterrupt: file.close() file.open(path,'w') dump(obj, file) file.close() raise
It seems silly to restart the operation if it is interrupted, so I am searching for a way to defer the interrupt. How do I do this?
In Python, there is no special syntax for the KeyboardInterrupt exception; it is handled in the usual try and except block. The code that potentially causes the problem is written inside the try block, and the 'raise' keyword is used to raise the exception, or the python interpreter raises it automatically.
The KeyboardInterrupt error occurs when a user manually tries to halt the running program by using the Ctrl + C or Ctrl + Z commands or by interrupting the kernel in the case of Jupyter Notebook.
Python allows us to set up signal -handlers so when a particular signal arrives to our program we can have a behavior different from the default. For example when you run a program on the terminal and press Ctrl-C the default behavior is to quit the program.
The following is a context manager that attaches a signal handler for SIGINT
. If the context manager's signal handler is called, the signal is delayed by only passing the signal to the original handler when the context manager exits.
import signal import logging class DelayedKeyboardInterrupt: 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()
Put the function in a thread, and wait for the thread to finish.
Python threads cannot be interrupted except with a special 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
See how the interrupt was deferred until the thread finished?
Here it is adapted to your use:
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With