The following program hangs the terminal such that it ignores Ctrl+C
. This is rather annoying since I have to restart the terminal every time one of the threads hang.
Is there any way to catch the KeyboardInterrupt
while waiting on an event?
import threading
def main():
finished_event = threading.Event()
startThread(finished_event)
finished_event.wait()#I want to stop the program here
print('done!')
def startThread(evt):
"""Start a thread that will trigger evt when it is done"""
#evt.set()
if __name__ == '__main__':
main()
Interpreter in python checks regularly for any interrupts while executing the program. In python, interpreter throws KeyboardInterrupt exception when the user/programmer presses ctrl – c or del key either accidentally or intentionally.
In computing, keyboard interrupt may refer to: A special case of signal (computing), a condition (often implemented as an exception) usually generated by the keyboard in the text user interface. A hardware interrupt generated when a key is pressed or released, see keyboard controller (computing)
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.
If you want to avoid polling, you can use the pause()
function of the signal module instead of finished_event.wait()
. signal.pause()
is a blocking function and gets unblocked when a signal is received by the process. In this case, when ^C is pressed, SIGINT signal unblocks the function. Note that the function does not work on Windows according to the documentation. I've tried it on Linux and it worked for me.
I came across this solution in this SO thread.
Update: On the current Python 3 finished_event.wait()
works on my Ubuntu machine (starting with Python 3.2). You don't need to specify the timeout
parameter, to interrupt it using Ctrl+C. You need to pass the timeout
parameter on CPython 2.
Here's a complete code example:
#!/usr/bin/env python3
import threading
def f(event):
while True:
pass
# never reached, otherwise event.set() would be here
event = threading.Event()
threading.Thread(target=f, args=[event], daemon=True).start()
try:
print('Press Ctrl+C to exit')
event.wait()
except KeyboardInterrupt:
print('got Ctrl+C')
There could be bugs related to Ctrl+C. Test whether it works in your environment.
Old polling answer:
You could try to allow the interpreter to run the main thread:
while not finished_event.wait(.1): # timeout in seconds
pass
If you just want to wait until the child thread is done:
while thread.is_alive():
thread.join(.1)
You could also patch the Event.wait() function in the following manner:
def InterruptableEvent():
e = threading.Event()
def patched_wait():
while not e.is_set():
e._wait(3)
e._wait = e.wait
e.wait = patched_wait
return e
>>> event = InterruptableEvent()
>>> try:
... event.wait()
... except KeyboardInterrupt:
... print "Received KeyboardInterrupt"
...
^CReceived KeyboardInterrupt
This works because wait() with a timeout argument will raise a KeyboardInterrupt.
Based on @Pete's answer, but with subclassing and using the actual Event.wait
method, just with smaller timeouts to allow handling of KeyboardInterrupt
s and such in between:
class InterruptableEvent(threading.Event):
def wait(self, timeout=None):
wait = super().wait # get once, use often
if timeout is None:
while not wait(0.01): pass
else:
wait(timeout)
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