Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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("\n\nExiting by CTRL+C.\n\n")     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 
like image 315
anandr Avatar asked Jun 26 '14 09:06

anandr


People also ask

How do you exit a Python script gracefully?

Graceful exit You can use the bash command echo $? to get the exit code of the Python interpreter.

How does Python handle Ctrl C?

You can handle CTRL + C by catching the KeyboardInterrupt exception. You can implement any clean-up code in the exception handler.

How do you end an endless loop in Python?

An infinite loop is a loop that runs indefinitely and it only stops with external intervention or when a break statement is found. You can stop an infinite loop with CTRL + C .


2 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() 
like image 74
mguijarr Avatar answered Sep 20 '22 15:09

mguijarr


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 
like image 25
Ashoka Lella Avatar answered Sep 20 '22 15:09

Ashoka Lella