I have a program that has quite a few functions, each running on a separate thread.
When the user presses Ctrl+C, only 1 thread crashes with an exception, but because of this, the whole program may not work correctly.
Of course, I can write this construction in each function:
try:
do_something()
except KeyboardInterrupt as e:
pass
but, as I said, there are many functions, perhaps there is an option not to prescribe this construction in each function?
Or is it possible to disable Ctrl+C interrupt in cmd settings?
For example, in the registry. The program creates its own registry key in HKEY_CURRENT_USER\Console\MyProgrammKey
UPD 1
signal.signal(signal.SIGINT, signal.SIG_IGN)
It helped in almost all cases except one: a thread that has an infinite loop with the input() function anyway interrupts.
UPD 2
Here is a sample code
import signal, time
from threading import Thread
def one():
while True:
inp = input("INPUT: ")
def two():
while True:
print("I just printing...")
time.sleep(1)
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal.SIG_IGN)
Thread(target=one).start()
Thread(target=two).start()
UPD 3
Screenshot of exception.
Ctrl+C will send SIGINT signal to program, so you could define a global signal handler to ignore that SIGINT, something like next:
test.py:
import signal, os, time
def handler(signum, frame):
pass
signal.signal(signal.SIGINT, handler)
time.sleep(10)
print("done")
During the program run, if you input Ctrl+c, the program will ignore it, and continue to run, finally print done:
$ python3 test.py
^Cdone
Related
Basically, I'm curious if it's possible to execute a block of python code "atomically" without being interrupted by a signal.
For instance, I want to perform operations in a loop, let's say:
for i in range(100):
do_stuff(1)
do_stuff(2)
do_stuff(3)
But I want to finish all of three do_stuff(1), do_stuff(2), do_stuff(3) if do_stuff(1) managed to start. Script should ignore CTRL+C, finish these three instructions and then terminate if SIGINT happened. All of 100 iterations does not have to be executed.
I believe it could be done with a custom signal handler
import signal
def handler(signum, frame):
# wait for the loop iteration finish and exit
signal.signal(signal.SIGINT, handler)
threads and synchronization but I have no idea how to implement it.
Is it possible?
If it is, can it be done nicely? With some kind of a context manager, for example?
for i in range(100):
with atomic_execution():
do_stuff(1)
do_stuff(2)
do_stuff(3)
Edit: in the meantime I created this:
import threading
import sys
import signal
class atomic_execution:
started = 0
execution_in_progress = threading.Lock()
def __enter__(self):
atomic_execution.execution_in_progress.acquire()
def __exit__(self, type, value, traceback):
atomic_execution.execution_in_progress.release()
def handler(signum, frame):
atomic_execution.execution_in_progress.acquire()
sys.exit(0)
signal.signal(signal.SIGINT, handler)
while True:
with atomic_execution():
print(1)
print(2)
print(3)
I am not sure if it's good, though.
This is the basic idea:
import signal
import time
stop = False
def sighandler(*unused):
global stop
print('signal caught')
stop = True
def main():
for i in range(10):
print('a')
time.sleep(0.5)
print('b')
time.sleep(0.5)
print('c')
time.sleep(0.5)
print()
if stop:
print('STOP')
break
if __name__ == '__main__':
signal.signal(signal.SIGINT, sighandler)
main()
I think it is not difficult to make an context manager for this purpose:
on enter:
save the current signal handler
install own handler setting a flag like in the example above
on exit:
restore the original signal handler
exit if the flag was set
But I do not like the idea, because you want to install the handler once before the loop and test the flag many times at each iteration.
In python, is it possible to make use of KeyboardInterrupt or CTRL+C to print a status message, possibly like printing content of a variable and then continuing with the execution?
Or will Interrupts always kill the process?
An example of what I would like to do:
def signal_handler(signum, frame):
global interrupted
interrupted = True
while true:
update(V)
if interrupted:
print V
You can do this using a signal handler:
import signal
def sigint_handler(signum, frame):
print "my_variable =", frame.f_locals.get("my_variable", None)
signal.signal(signal.SIGINT, sigint_handler)
Now interrupting the script calls the handler, which prints the variable, fishing it out of the current stack frame. The script then continues.
It can be done. The signal library provides this functionality, and it pretty much goes the way you prototyped.
import signal
interrupted = False
def signal_handler(signum, frame):
global interrupted
interrupted = True
signal.signal(signal.SIGINT, signal_handler)
while true:
update(V)
if interrupted:
print V
Pressing ctrl+c raises KeyboardInterrupt.
Catch KeyboardInterrupt and print a message from it, or use a state variable like you have above.
See: Capture Control-C in Python
I would like the user to use control-c to close a script, but when control-c is pressed it shows the error and reason for close (which make sense). Is there a way to have my own custom output to the screen rather than what is shown? Not sure how to handle that specific error.
You could use try..except to catch KeyboardInterrupt:
import time
def main():
time.sleep(10)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('bye')
use the signal module to define a handler for the SIGINT signal:
import signal
import sys
def sigint_handler(signal_number, stack_frame):
print('caught SIGINT, exiting')
sys.exit(-1)
signal.signal(signal.SIGINT, sigint_handler)
raw_input('waiting...')
For general purpose code, handling the KeyboardInterrupt should suffice. For advanced code, such as threading, it is a whole different story. Here's a simple example.
http://docs.python.org/2/library/exceptions.html#exceptions.KeyboardInterrupt
try:
while 1:
x = raw_input("Type something or press CTRL+C to end: ")
print repr(x)
except KeyboardInterrupt:
print "\nWe're done here."
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()
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 KeyboardInterrupts 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)
Is there anyway I can make my script execute one of my functions when Ctrl+c is hit when the script is running?
Take a look at signal handlers. CTRL-C corresponds to SIGINT (signal #2 on posix systems).
Example:
#!/usr/bin/env python
import signal
import sys
def signal_handler(signal, frame):
print("You pressed Ctrl+C - or killed me with -2")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print("Press Ctrl+C")
signal.pause()
Sure.
try:
# Your normal block of code
except KeyboardInterrupt:
# Your code which is executed when CTRL+C is pressed.
finally:
# Your code which is always executed.
Use the KeyboardInterrupt exception and call your function in the except block.