[Tutor] KeyboardInterrupt handler

Michael P. Reilly arcege@speakeasy.net
Sat, 2 Feb 2002 09:55:42 -0500


On Thu, Jan 31, 2002 at 05:15:01PM -0500, Lloyd Kvam wrote:
> Would it be practical to write a KeyboardInterrupt handler that "resumed" execution?
> 
> Since the debugger seems to do this kind of thing, I assume the traceback object and
> its frame list could be used to accomplish a resume.  (This is more idle curiosity than
> any real need.)  Searching Google, my books, and the manual failed to come up with
> anything useful.

You can't restart Python from a frame (there is a version of Python
called "Microthreads" where you can handle continuations, but not in
Python itself).

The debugger doesn't do what you might think it does.  There is a hook
in the Python interpreter that can run code.  The debugger carefully
manages what code runs via this hook, and it can capture what exceptions
are being handled.
(cf. <URL: http://www.python.org/doc/current/lib/debugger-hooks.html>)

Typically, you'd write code that would reset what you are doing and
continue in whatever you were doing.  Most importantly, if you have
something you are doing, then wrap it in a try-finally statement.
For example, if you had a command interpreter.

while 1:
  try:
    cmd = raw_input(prompt)
    ...
    if cmd == 'change':
      # call something that could potentially break if the user
      # interrupted
      make_change()
  # catch the exception and reset the interpreter
  except KeyboardInterrupt:
    reset()  # maybe just print so the prompt starts in the first column

def make_change():
  try:
    os.rename('file', 'file.temp')
    inf = open('file.temp', 'rb')
    outf = open('file', 'wb')
    modify_file(inf, outf)
    inf.close()
    outf.close()
  # if there is an error, then the files will still be open,
  # and we will back-out the changes
  # in this case, if the user types ^C, we move the files back and
  # the exception gets back to the loop above to reset the command
  finally:
    if not outf.closed: # we didn't finish it
      outf.close()
      try:
        os.remove('file')
      except:
        pass
      inf.close()
      os.rename('file.temp', 'file')
    # otherwise, things completed successfully

Most database extensions will have provisions for rolling back
transactions.  But you can do most everything in a similar way at
some level.  I had to do this same exact thing ten years ago, writing
a bullet-proof command interpreter in Bourne shell.  Using the
debugging mechanism is not going to help much to handle rolling back
operations like the above.

Your original question is: would it be practical.  My response is: if
the user interrupted the program for a reason... should you resume at
the same place?  That might help you answer your question.

  -Arcege