1

Is there any way to defer an exception coming from an external source for the duration of a certain block of code? For example:

some_statement_one
some_statement_two
# Begin suppression of KeyboardInterrupt
with KeyboardInterrupt suppressing context:
    important_statement_one
    important_statement_two
    important_statement_three

some_statement_three

What I would like to be able to do is to delay reception of a KeyboardInterrupt (or whatever error(s) I specify) until after then end of the important block. That is, if during the execution of important_statement_one the user sends a keyboard interrupt, the code will continue normally until after it has finished important_statement_three. Then, the KeyboardInterrupt will be received as if it had been sent right then.

My concern is only with an exception that is generated external to the code being executed, such as KeyboardInterrupts, SystemExits, etc. Is there even a meaningful way to distinguish between an exception raised by code being executed and an exception raised due to something external? I probably can't do what I'm trying to do unless there is.

Some additional information: I'm using the technique suggested in Is there any way to kill a Thread in Python? to send exceptions to threads. Does raising an exception like this make a difference?

I can't really use try/except blocks for this - I'm some information between threads, so if the exception is sent on the line where the information is passed then the information will be lost.

Community
  • 1
  • 1
Rob Watts
  • 6,866
  • 3
  • 39
  • 58
  • 1
    What do you mean *from an external source*? You mean the exception being raised at all in other source code? That you certainly cannot do. – Martijn Pieters Jun 13 '14 at 18:53
  • Does it make more sense to say an exception caused somewhere else (for example, a keyboard interrupt caused by the user hitting ctrl-c)? – Rob Watts Jun 13 '14 at 18:57
  • You can suppress interrupts; Python has installed a signal handler to translate the `SIGINT` interrupt into an exception, but you can override that handler. See https://docs.python.org/2/library/signal.html – Martijn Pieters Jun 13 '14 at 19:00
  • I should have said "defer" instead of "suppress". Overriding the signal handler would definitely work for keyboard interrupts, though I was hoping for a more general solution. – Rob Watts Jun 13 '14 at 19:34
  • But the most common use of exceptions is to communicate that a piece of code cannot continue; how do you expect the rest of the code to cope with the missing data that should have been provided by whatever threw the exception instead? – Martijn Pieters Jun 13 '14 at 21:15
  • @MartijnPieters I'm only worried about cases where there would not be any missing data. Where there would be missing data (i.e my code throws the error) then it should be handled normally. Take a look at the answer I posted - that would be what I'm looking for if it could handle more than just keyboard interrupts. – Rob Watts Jun 13 '14 at 21:48
  • But anything else throwing exceptions are **not external signals**. – Martijn Pieters Jun 13 '14 at 21:50

2 Answers2

2

I think you want to override the default handler for the sigint signal.

Here is a simple way to do so.

some_statement_one
some_statement_two

# Save sigint handler
original_sigint_handler = signal.getsignal(signal.SIGINT)

# Set sigint handler to a function that does nothing with it
signal.signal(signal.SIGINT, lambda: None)

important_statement_one
important_statement_two
important_statement_three

# Restore handler
signal.signal(signal.SIGINT, original_sigint_handler)

some_statement_three

You may want to wrap this in a class and use enter and exit functions to get a cleaner code. Your code would them look like you wanted (using "with")

Read: Explaining Python's '__enter__' and '__exit__'

Community
  • 1
  • 1
Eloims
  • 5,106
  • 4
  • 25
  • 41
0

Note that this doesn't fully answer my question, but this works specifically for deferring a keyboard interrupt.


As suggested by Martijn Pieters, a signal handler can be used to handle a keyboard interrupt before it becomes an exception that needs to be caught:

import signal
import threading

interruptLock = threading.Lock()

def handleInterrupt(signum, frame):
    with interruptLock:
        raise KeyboardInterrupt()

signal.signal(signal.SIGINT, handleInterrupt)

Then in the important code:

some_statement_one
some_statement_two
with interruptLock:
    important_statement_one
    ...

This only allows one thread in the important section at a time, but it defers the exception until the interrupt lock has been released, so it should not be interrupted during that section.

If you want to have multiple threads going through the important section at a time, you might want to use a reader-writer lock. One implementation for a reader-writer lock can be found here

Rob Watts
  • 6,866
  • 3
  • 39
  • 58