7

My while loop does not exit when Ctrl+C is pressed. It seemingly ignores my KeyboardInterrupt exception. The loop portion looks like this:

while True:
  try:
    if subprocess_cnt <= max_subprocess:
      try:
        notifier.process_events()
        if notifier.check_events():
          notifier.read_events()
      except KeyboardInterrupt:
        notifier.stop()
        break
    else:
      pass
  except (KeyboardInterrupt, SystemExit):
    print '\nkeyboardinterrupt found!'
    print '\n...Program Stopped Manually!'
    raise

Again, I'm not sure what the problem is but my terminal never even prints the two print alerts I have in my exception. Can someone help me figure this problem out?

jcollado
  • 39,419
  • 8
  • 102
  • 133
sadmicrowave
  • 39,964
  • 34
  • 108
  • 180
  • 2
    Your first `except KeyboardInterrupt` catches the exception. It won't be visible to the second `except (KeyboardInterrupt, SystemExit)` if you don't re-raise it. – eumiro Dec 27 '11 at 14:23
  • @eumiro - I commented out the first KeyboardInterrupt and replaced the contents of the exception with 'pass' but I'm still experiencing the same problem. Ctrl+C is being ignored (ps aux shows the process still running as well) – sadmicrowave Dec 27 '11 at 14:30
  • @eumiro I've also tried to re-raise the KeyboardInterrupt exception by adding `raise KeyboardInterrupt()` within the first `except KeyboardInterrupt:` however I'm still experiencing the same issue. – sadmicrowave Dec 27 '11 at 14:50

1 Answers1

15

Replace your break statement with a raise statement, like below:

while True:
  try:
    if subprocess_cnt <= max_subprocess:
      try:
        notifier.process_events()
        if notifier.check_events():
          notifier.read_events()
      except KeyboardInterrupt:
        notifier.stop()
        print 'KeyboardInterrupt caught'
        raise  # the exception is re-raised to be caught by the outer try block
    else:
      pass
  except (KeyboardInterrupt, SystemExit):
    print '\nkeyboardinterrupt caught (again)'
    print '\n...Program Stopped Manually!'
    raise

The two print statements in the except blocks should execute with '(again)' appearing second.

Zoran Pavlovic
  • 1,166
  • 2
  • 23
  • 38
wberry
  • 18,519
  • 8
  • 53
  • 85
  • 1
    While you didn't ask about this, the `pass` statement in your code creates what is called a spin-lock. Spin-locks consume CPU needlessly and can impact performance of the overall system. There are ways to avoid them. Investigate using `Queue.Queue` objects for communicating between threads, and `select.select` or the `multiprocessing` module for communicating between processes. – wberry Dec 27 '11 at 15:07
  • So, it occurs to me that my main problem (I thought to be unrelated) is I have forked my script twice to 'daemonize' it. That being the case, KeyboardInterrupt's no longer are connected to the same terminal (however, my script will still print outputs to the active terminal). Is there a way to still utilize KeyboardInterrupt (or another way to end my script manually) within a daemonized program? – sadmicrowave Dec 27 '11 at 15:22
  • currently I have been searching for the processid within the `ps aux` output and executing a `sudo kill [pid]`; however, this does not gracefully clean up my code before killing it. I need to close database connections and remove inotify watches before killing the program. – sadmicrowave Dec 27 '11 at 15:24
  • If you're calling `os.fork` directly, or if the API you use gives you the child process' pid, then you can `os.kill(childpid, signal.SIGTERM)` explicitly within your `except (KeyboardInterrupt)` block. That will have the effect of "passing" the control-C to the child process. – wberry Dec 27 '11 at 15:27
  • But if it's a true daemon process, that means the parent has exited leaving it to run on its own, and you will have to use a "pid file", i.e. the daemon writes its own pid to a file in a known location. A shell script could then `kill -TERM \`cat $PIDFILE\`` to terminate the daemon process. – wberry Dec 27 '11 at 15:29
  • I used this tutorial for daemonizing my script: http://motomastyle.com/daemonizing-a-python-script/ – sadmicrowave Dec 27 '11 at 15:54
  • If my problem is the KeyboardInterrupt not triggering because the script has been forked, then how would including os.kill() or a os.popen() command inside my KeyboardInterrupt block fire at all? The KeyboardInterrupt is not triggering, therefore, nothing inside the exception block will trigger either... – sadmicrowave Dec 27 '11 at 16:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/6162/discussion-between-sadmicrowave-and-wberry) – sadmicrowave Dec 27 '11 at 16:07
  • As it turns out I was looking for `sudo kill -INT [pid]`. That sends SIGINT to the process and python interprets it as KeyboardInterrupt. So now I can use the KeyboardInterrupt exception for handling program shutdown events. Which is what I was after. You definitely led me in the right direction though with some help from @wardi on irc.freenode.net/#python – sadmicrowave Dec 27 '11 at 19:23