Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python curses dilemma

I'm playing around a little with Python and curses.

When I run

import time
import curses

def main():
    curses.initscr()
    curses.cbreak()
    for i in range(3):
        time.sleep(1)
        curses.flash()
        pass
    print( "Hello World" )
    curses.endwin()

if __name__ == '__main__':
    main()

if I wait all the way through, curses.endwin() gets called so everything works out fine. However, if I cut it short with Ctrl-C, curses.endwin() never gets called so it screws up my terminal session.

What is the proper way to handle this situation? How can I make sure that no matter how I try to end/interrupt the program (e.g. Ctrl-C, Ctrl-Z), it doesn't mess up the terminal?

like image 950
math4tots Avatar asked Mar 24 '12 18:03

math4tots


2 Answers

My advice: For testing purposes, call your script using a simple wrapper shell script; have the shell script perform a reset command to bring your terminal settings back into a usable state:

#!/bin/sh
eval "$@"
stty -sane
reset

... call that as run.sh and be happy. This should run your command almost exactly as your shell would if you entered the arguments as a command (more exactly if you wrap the arguments in hard quotes).

To ensure that your program will leave the terminal in a robust state, in the the face of uncaught exceptions and abnormal terminations ... either use the curses.wrapper() method to call your top level entry point (probably main() or whatever main_curses_ui() you choose to implement) or wrap your code in your own sequence of curses.* methods to restore cursor visibility, restore "cbreak" (canonical/cooked input) mode, restore the normal "echo" settings and whatever else you may have mucked with.

You can also use the Python: atexit Handlers to register all your clean-up actions. But there might still be cases where your code doesn't get called --- some sorts of non-catchable signals and any situation where os._exit() is invoked.

My little shell script wrapper should be fairly robust even in those cases.

like image 174
Jim Dennis Avatar answered Sep 24 '22 15:09

Jim Dennis


You can:

  • wrap your code in a try/finally block that calls curses.endwin()
  • capture the interrupt signal specifically via the signal library
  • use the atexit library.

The first option is probably the simplest for a basic case (if you're not running much code).

The second option is the most specific, if you want to do something special for Ctrl+C.

The last option is the most robust if you always want to do certain shutdown actions, no matter how your program is ending.

like image 24
Amber Avatar answered Sep 20 '22 15:09

Amber