How to terminate loop gracefully when CTRL+C was pressed in python
Question:
I’m rather new to python and I’m stuck with the following problem.
I have a script that processes files one-by-one and writes output into separate files according to input file name. Sometimes I need to break the script, but I’d like to let it finish processing current file and then terminate (to avoid result files with incomplete information). How to code this behavior in python?
Here is what I tried.
a) Try-except block
x = 1
print "Script started."
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
except KeyboardInterrupt:
print "Bye"
print "x=",x
sys.exit()
sys.exit()
Output:
Script started.
Processing file # 1 started... finished.
Processing file # 2 started... finished.
Processing file # 3 started... Bye
x= 3
Iteration #3 is not finished gracefully.
b) sys.excepthook
OriginalExceptHook = sys.excepthook
def NewExceptHook(type, value, traceback):
global Terminator
Terminator = True
if type == KeyboardInterrupt:
#exit("nExiting by CTRL+C.") # this line was here originally
print("nnExiting by CTRL+C.nn")
else:
OriginalExceptHook(type, value, traceback)
sys.excepthook = NewExceptHook
global Terminator
Terminator = False
x = 1
while True:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
if Terminator:
print "I'll be back!"
break
print "Bye"
print "x=",x
sys.exit()
Output:
Script started.
Processing file # 1 started... finished.
Processing file # 2 started... finished.
Processing file # 3 started...
Exiting by CTRL+C.
Iteration #3 is not finished gracefully.
UPD#1
@mguijarr , I slightly modified code like this:
import time, sys
x = 1
print "Script started."
stored_exception=None
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print "Processing file #",x,"part two...",
time.sleep(1)
print " finished."
if stored_exception:
break
x += 1
except KeyboardInterrupt:
print "[CTRL+C detected]",
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
The output is (tested using "Python 2.7.6 :: Anaconda 2.0.0 (64-bit)" on Win7-64bit):
Script started.
Processing file # 1 started... Processing file # 1 part two... finished.
Processing file # 2 started... Processing file # 2 part two... finished.
Processing file # 3 started... [CTRL+C detected] Processing file # 3 started... Processing file # 3 part two... finished.
Bye
x= 3
Traceback (most recent call last):
File "test2.py", line 12, in <module>
time.sleep(1)
KeyboardInterrupt
In this case iteration #3 was effectively restarted, which looks odd and is not a desired behavior. Is it possible to avoid this?
I removed commas in ‘print’ statements and added more stuff to see that iteration is actually restarted:
import time, sys
x = 1
y = 0
print "Script started."
stored_exception=None
while True:
try:
y=x*1000
y+=1
print "Processing file #",x,y,"started..."
y+=1
# do something time-cosnuming
y+=1
time.sleep(1)
y+=1
print "Processing file #",x,y,"part two..."
y+=1
time.sleep(1)
y+=1
print " finished.",x,y
y+=1
if stored_exception:
break
y+=1
x += 1
y+=1
except KeyboardInterrupt:
print "[CTRL+C detected]",
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
print "y=",y
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
and the output is:
Script started.
Processing file # 1 1001 started...
Processing file # 1 1004 part two...
finished. 1 1006
Processing file # 2 2001 started...
Processing file # 2 2004 part two...
[CTRL+C detected] Processing file # 2 2001 started...
Processing file # 2 2004 part two...
finished. 2 2006
Bye
x= 2
y= 2007
Traceback (most recent call last):
File "test2.py", line 20, in <module>
time.sleep(1)
KeyboardInterrupt
Answers:
I would simply use an exception handler, which would catch KeyboardInterrupt
and
store the exception. Then, at the moment an iteration is finished, if an exception
is pending I would break the loop and re-raise the exception (to let normal exception
handling a chance to happen).
This works (tested with Python 2.7):
x = 1
print "Script started."
stored_exception=None
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print " finished."
if stored_exception:
break
x += 1
except KeyboardInterrupt:
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
EDIT: as it has been spotted in the comments, this answer is not satisfying for the original poster, here is a solution based on threads:
import time
import sys
import threading
print "Script started."
class MyProcessingThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print " finished."
for x in range(1,4):
task = MyProcessingThread()
task.start()
try:
task.join()
except KeyboardInterrupt:
break
print "Bye"
print "x=",x
sys.exit()
You can write a signal handling function
import signal,sys,time
terminate = False
def signal_handling(signum,frame):
global terminate
terminate = True
signal.signal(signal.SIGINT,signal_handling)
x=1
while True:
print "Processing file #",x,"started..."
time.sleep(1)
x+=1
if terminate:
print "I'll be back"
break
print "bye"
print x
pressing Ctrl+c sends a SIGINT interrupt which would output:
Processing file # 1 started...
Processing file # 2 started...
^CI'll be back
bye
3
I feel that creating a class with a state that handles user exceptions is a bit more elegant since I don’t have to mess with global variables that don’t work across different modules
import signal
import time
class GracefulExiter():
def __init__(self):
self.state = False
signal.signal(signal.SIGINT, self.change_state)
def change_state(self, signum, frame):
print("exit flag set to True (repeat to exit now)")
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.state = True
def exit(self):
return self.state
x = 1
flag = GracefulExiter()
while True:
print("Processing file #",x,"started...")
time.sleep(1)
x+=1
print(" finished.")
if flag.exit():
break
Late to the party, but this was my solution:
#!/usr/bin/env python
# -*- coding: utf8 -*-
import sys
import signal
import time
interrupted = False
def main():
global interrupted
oldHandler = signal.signal(signal.SIGINT, handle_interrupt)
x = 1
print "Script started"
while True:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
if interrupted:
break
signal.signal(signal.SIGINT, oldHandler)
def handle_interrupt(sig, frame):
global interrupted
interrupted = True
if __name__ == '__main__':
sys.exit(main())
I’m rather new to python and I’m stuck with the following problem.
I have a script that processes files one-by-one and writes output into separate files according to input file name. Sometimes I need to break the script, but I’d like to let it finish processing current file and then terminate (to avoid result files with incomplete information). How to code this behavior in python?
Here is what I tried.
a) Try-except block
x = 1
print "Script started."
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
except KeyboardInterrupt:
print "Bye"
print "x=",x
sys.exit()
sys.exit()
Output:
Script started.
Processing file # 1 started... finished.
Processing file # 2 started... finished.
Processing file # 3 started... Bye
x= 3
Iteration #3 is not finished gracefully.
b) sys.excepthook
OriginalExceptHook = sys.excepthook
def NewExceptHook(type, value, traceback):
global Terminator
Terminator = True
if type == KeyboardInterrupt:
#exit("nExiting by CTRL+C.") # this line was here originally
print("nnExiting by CTRL+C.nn")
else:
OriginalExceptHook(type, value, traceback)
sys.excepthook = NewExceptHook
global Terminator
Terminator = False
x = 1
while True:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
if Terminator:
print "I'll be back!"
break
print "Bye"
print "x=",x
sys.exit()
Output:
Script started.
Processing file # 1 started... finished.
Processing file # 2 started... finished.
Processing file # 3 started...
Exiting by CTRL+C.
Iteration #3 is not finished gracefully.
UPD#1
@mguijarr , I slightly modified code like this:
import time, sys
x = 1
print "Script started."
stored_exception=None
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print "Processing file #",x,"part two...",
time.sleep(1)
print " finished."
if stored_exception:
break
x += 1
except KeyboardInterrupt:
print "[CTRL+C detected]",
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
The output is (tested using "Python 2.7.6 :: Anaconda 2.0.0 (64-bit)" on Win7-64bit):
Script started.
Processing file # 1 started... Processing file # 1 part two... finished.
Processing file # 2 started... Processing file # 2 part two... finished.
Processing file # 3 started... [CTRL+C detected] Processing file # 3 started... Processing file # 3 part two... finished.
Bye
x= 3
Traceback (most recent call last):
File "test2.py", line 12, in <module>
time.sleep(1)
KeyboardInterrupt
In this case iteration #3 was effectively restarted, which looks odd and is not a desired behavior. Is it possible to avoid this?
I removed commas in ‘print’ statements and added more stuff to see that iteration is actually restarted:
import time, sys
x = 1
y = 0
print "Script started."
stored_exception=None
while True:
try:
y=x*1000
y+=1
print "Processing file #",x,y,"started..."
y+=1
# do something time-cosnuming
y+=1
time.sleep(1)
y+=1
print "Processing file #",x,y,"part two..."
y+=1
time.sleep(1)
y+=1
print " finished.",x,y
y+=1
if stored_exception:
break
y+=1
x += 1
y+=1
except KeyboardInterrupt:
print "[CTRL+C detected]",
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
print "y=",y
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
and the output is:
Script started.
Processing file # 1 1001 started...
Processing file # 1 1004 part two...
finished. 1 1006
Processing file # 2 2001 started...
Processing file # 2 2004 part two...
[CTRL+C detected] Processing file # 2 2001 started...
Processing file # 2 2004 part two...
finished. 2 2006
Bye
x= 2
y= 2007
Traceback (most recent call last):
File "test2.py", line 20, in <module>
time.sleep(1)
KeyboardInterrupt
I would simply use an exception handler, which would catch KeyboardInterrupt
and
store the exception. Then, at the moment an iteration is finished, if an exception
is pending I would break the loop and re-raise the exception (to let normal exception
handling a chance to happen).
This works (tested with Python 2.7):
x = 1
print "Script started."
stored_exception=None
while True:
try:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print " finished."
if stored_exception:
break
x += 1
except KeyboardInterrupt:
stored_exception=sys.exc_info()
print "Bye"
print "x=",x
if stored_exception:
raise stored_exception[0], stored_exception[1], stored_exception[2]
sys.exit()
EDIT: as it has been spotted in the comments, this answer is not satisfying for the original poster, here is a solution based on threads:
import time
import sys
import threading
print "Script started."
class MyProcessingThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
print " finished."
for x in range(1,4):
task = MyProcessingThread()
task.start()
try:
task.join()
except KeyboardInterrupt:
break
print "Bye"
print "x=",x
sys.exit()
You can write a signal handling function
import signal,sys,time
terminate = False
def signal_handling(signum,frame):
global terminate
terminate = True
signal.signal(signal.SIGINT,signal_handling)
x=1
while True:
print "Processing file #",x,"started..."
time.sleep(1)
x+=1
if terminate:
print "I'll be back"
break
print "bye"
print x
pressing Ctrl+c sends a SIGINT interrupt which would output:
Processing file # 1 started...
Processing file # 2 started...
^CI'll be back
bye
3
I feel that creating a class with a state that handles user exceptions is a bit more elegant since I don’t have to mess with global variables that don’t work across different modules
import signal
import time
class GracefulExiter():
def __init__(self):
self.state = False
signal.signal(signal.SIGINT, self.change_state)
def change_state(self, signum, frame):
print("exit flag set to True (repeat to exit now)")
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.state = True
def exit(self):
return self.state
x = 1
flag = GracefulExiter()
while True:
print("Processing file #",x,"started...")
time.sleep(1)
x+=1
print(" finished.")
if flag.exit():
break
Late to the party, but this was my solution:
#!/usr/bin/env python
# -*- coding: utf8 -*-
import sys
import signal
import time
interrupted = False
def main():
global interrupted
oldHandler = signal.signal(signal.SIGINT, handle_interrupt)
x = 1
print "Script started"
while True:
print "Processing file #",x,"started...",
# do something time-cosnuming
time.sleep(1)
x += 1
print " finished."
if interrupted:
break
signal.signal(signal.SIGINT, oldHandler)
def handle_interrupt(sig, frame):
global interrupted
interrupted = True
if __name__ == '__main__':
sys.exit(main())