How to terminate loop gracefully when CTRL+C was pressed in python

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

Tags:

Python

Loops