Search code examples
pythonerror-handlingsignalsinteractive

How to get back to the input prompt in an interactive python program and still be able to abort running operations


I wrote an interactive python program with more than 100 functions which do various computations I'm interested in.

The user enters a function name at the input prompt, which is then executed with results either displayed as a return value or as a side effect. I want to be able to abort long running functions, via a control C, without killing the program.

This part I've accomplished. For example, as shown below, if a control-C is entered while function2(20) is running, anytime before it finishes, the function is aborted and the input prompt is displayed again.

However if I happen, by accident, to enter control-C at the input prompt, the program is killed. I don't want that to happen; rather I want to just return back to the input prompt. Since these two features are part of standard python interpreters, it should be possible to do what I want.

Here's a skeleton version of what I've done, called skeleton.

#!/Users/richard.gostanian/anaconda3/bin/python

import sys
import signal
import time

def trivial_function(n):
   return n

def function2(n):
    for i in range(1,n):
       print(i)
       time.sleep(1)
    return n

while True:
   entered2 = input("Enter function with arguments: ")
   entered=entered2.strip()   # remove preceding and trailing whitespace from entered2

   if entered == 'q':
      print()
      quit()
   elif entered == "":  # go back to the input prompt
      print()
      continue
   else:
      try:
        result=eval(entered)
        print("\n   {} = {} \n".format(entered,result))
        continue
      except KeyboardInterrupt:
           print()
           continue

Here is the output if you type control-C while function2(20) is running

Enter function: function2(20)
1
2
3
4
^C
Enter function: 

And here is the output if you type control-C at the input prompt

Enter function: ^CTraceback (most recent call last):
  File "/Users/richard/home/richard/bin/./skeleton", line 17, in <module>
    entered2 = input("Enter function: ")
               ^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt

bash >

How do I modify this to not kill the program in the second case?


Solution

  • Put try/except around the entire loop body, not just the call to eval().

    while True:
        try:
            entered2 = input("Enter function with arguments: ")
            entered=entered2.strip()   # remove preceding and trailing whitespace from entered2
        
            if entered == 'q':
                print()
                quit()
            elif entered == "":  # go back to the input prompt
                print()
            else:
                result=eval(entered)
                print("\n   {} = {} \n".format(entered,result))
        except KeyboardInterrupt:
            print()
    

    You don't need all those continue statements. A loop automatically continues unless the while condition is false or you use break to get out. You only need to use continue if you want to skip over the rest of the loop body and start the next iteration, but there's nothing to skip over.