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
I am having a main program which is defined like this:
main.py
def main():
try:
registry.start_server()
except:
print("Shutting down the program")
pass
if __name__ == '__main__':
main()
registry.start_server() is the method in another module which looks like this:
def start_server():
t_server = threading.Thread(target=server.start)
t_server.start()
try:
t_server.join()
except KeyboardInterrupt:
print("Error")
raise ValueError
finally:
fp.close()
server.start is the method in another module which does some listening work in a while(True) manner. I am not sure how to stop the whole program when clicking Stop in PyCharm which is Ctrl + C (Signal). I tried with Event but without success. I get to the main.py by raising an exception when the signal gets caught but that does not terminate the whole program. It shows Waiting for program to detach. The only way is to use SIGKILL. I don't understand where does the program keeps hanging? I have also tried calling sys.exit(0) when the signal gets caught and creating the thread as Deamon but that didnt help either.
EDIT
While True method in another module
def start(self, event):
try:
while True:
if event.is_set():
if self.pubsub.channels:
print("It enters here")
message = self.pubsub.get_message(True)
if message:
.
.
.
else:
return
To solve the problem, all you need to do is:
let the child-thread exit, and
let main thread join the child-thread.
is there any way to catch an exception for an unexpected shutdown of program in python ?
let say I am running a python script in a console then I don't press control+c to stop the program but rather just click the close button of the console is there any way to catch the error before the console close?
like this:
try:
print("hello")
except KeyboardInterrupt:
exit()
except UnexpectedClose:
print("unexpected shutoff")
exit()
thanks in advance
Following the link I put in the comment above already and reading here that forced closing is sending a SIGHUP this modified version writes an output file when the terminal window is closed and the python process is "killed".
Note, I just combined information (as cited) available on SE.
import signal
import time
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
signal.signal(signal.SIGHUP, self.exit_gracefully)
def exit_gracefully(self,signum, frame):
with open('kill.txt', 'w') as fpntr:
fpntr.write('killed')
self.kill_now = True
if __name__ == '__main__':
killer = GracefulKiller()
while True:
time.sleep(1)
print("doing something in a loop ...")
if killer.kill_now:
break
print "End of the program. I was killed gracefully :)"
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."
While using python's cmd.Cmd to create a custom CLI, how do I tell the handler to abort the current line and give me a new prompt?
Here is a minimal example:
# console_min.py
# run: 'python console_min.py'
import cmd, signal
class Console(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "[test] "
signal.signal(signal.SIGINT, handler)
def do_exit(self, args):
return -1
def do_EOF(self, args):
return self.do_exit(args)
def preloop(self):
cmd.Cmd.preloop(self)
self._hist = []
self._locals = {}
self._globals = {}
def postloop(self):
cmd.Cmd.postloop(self)
print "Exiting ..."
def precmd(self, line):
self._hist += [ line.strip() ]
return line
def postcmd(self, stop, line):
return stop
def emptyline(self):
return cmd.Cmd.emptyline(self)
def handler(self, signum, frame):
# self.emptyline() does not work here
# return cmd.Cmd.emptyline(self) does not work here
print "caught ctrl+c, press return to continue"
if __name__ == '__main__':
console = Console()
console.cmdloop()
Further help is greatly appreciated.
Original Question and more details:
[Currently the suggestions below have been integrated into this question -- still searching for an answer. Updated to fix an error.]
I've since tried moving the handler to a function outside the loop to see if it were more flexible, but it does not appear to be.
I am using python's cmd.Cmd module to create my own command line interpreter to manage interaction with some software. I often press ctrl+c expecting popular shell-like behavior of returning a new prompt without acting on whatever command was typed. However, it just exits. I've tried to catch KeyboardInterrupt exceptions at various points in the code (preloop, etc.) to no avail. I've read a bit on sigints but don't quite know how that fits in here.
UPDATE: From suggestions below, I tried to implement signal and was able to do so such that ctrl+c now doesn't exit the CLI but does print the message. However, my new issue is that I cannot seem to tell the handler function (see below) to do much beside print. I would like ctrl+c to basically abort the current line and give me a new prompt.
I'm currently working on a creation of a Shell by using the Cmd module. I've been confronted with the same issue, and I found a solution.
Here is the code:
class Shell(Cmd, object)
...
def cmdloop(self, intro=None):
print(self.intro)
while True:
try:
super(Shell, self).cmdloop(intro="")
break
except KeyboardInterrupt:
print("^C")
...
Now you have a proper KeyboardInterrupt (aka CTRL-C) handler within the shell.
Instead of using signal handling you could just catch the KeyboardInterrupt that cmd.Cmd.cmdloop() raises. You can certainly use signal handling but it isn't required.
Run the call to cmdloop() in a while loop that restarts itself on an KeyboardInterrupt exception but terminates properly due to EOF.
import cmd
import sys
class Console(cmd.Cmd):
def do_EOF(self,line):
return True
def do_foo(self,line):
print "In foo"
def do_bar(self,line):
print "In bar"
def cmdloop_with_keyboard_interrupt(self):
doQuit = False
while doQuit != True:
try:
self.cmdloop()
doQuit = True
except KeyboardInterrupt:
sys.stdout.write('\n')
console = Console()
console.cmdloop_with_keyboard_interrupt()
print 'Done!'
Doing a CTRL-c just prints a new prompt on a new line.
(Cmd) help
Undocumented commands:
======================
EOF bar foo help
(Cmd) <----- ctrl-c pressed
(Cmd) <------ctrl-c pressed
(Cmd) ddasfjdfaslkdsafjkasdfjklsadfljk <---- ctrl-c pressed
(Cmd)
(Cmd) bar
In bar
(Cmd) ^DDone!
You can catch the CTRL-C signal with a signal handler. If you add the code below, the interpreter refuses to quit on CTRL-C:
import signal
def handler(signum, frame):
print 'Caught CTRL-C, press enter to continue'
signal.signal(signal.SIGINT, handler)
If you don't want to press ENTER after each CTRL-C, just let the handler do nothing, which will trap the signal without any effect:
def handler(signum, frame):
""" just do nothing """
In response to the following comment in this answer:
This appears to be converging on a solution, but I don't know how to integrate it into my own code (above). I have to figure out the 'super' line. I need to try and get this working at some point in the future.
super() would work in this answer if you have your class extend object in addition to cmd.Cmd. Like this:
class Console(cmd.Cmd, object):
I wanted to do the same, so I searched for it and created basic script for understanding purposes, my code can be found on my GitHub
So, In your code,
instead of this
if __name__ == '__main__':
console = Console()
console.cmdloop()
Use this,
if __name__ == '__main__':
console = Console()
try:
console.cmdloop()
except KeyboardInterrupt:
print ("print sth. ")
raise SystemExit
Hope this will help you out. well, it worked for me. 😀
You can check out the signal module: http://docs.python.org/library/signal.html
import signal
oldSignal = None
def handler(signum, frame):
global oldSignal
if signum == 2:
print "ctrl+c!!!!"
else:
oldSignal()
oldSignal = signal.signal(signal.SIGINT, handler)
Just add this inside the class Console(cmd.Cmd):
def cmdloop(self):
try:
cmd.Cmd.cmdloop(self)
except KeyboardInterrupt as e:
self.cmdloop()
Forget all the other stuff. This works. It doesn't make loops inside of loops. As it catches KeyboardInterrupt it calls do_EOF but only will execute the first line; since your first line in do_EOF is return do_exit this is fine.
do_exit calls postloop.
However, again, it only executes the first line after cmd.Cmd.postloop(self). In my program this is print "\n". Strangely, if you SPAM ctrl+C you will eventually see it print the 2nd line usually only printed on ACTUAL exit (ctrl+Z then enter, or typing in exit).
I prefer the signal method, but with just pass. Don't really care about prompting the user with anything in my shell environment.
import signal
def handler(signum, frame):
pass
signal.signal(signal.SIGINT, handler)