Search code examples
pythonterminalcursesstty

Python curses unsets 'onlcr' and breaks my terminal; how to reset *properly*


I have the following basic curses implementation in Python3.

#!/usr/bin/env python3

import curses
import time
from curses import wrapper

stdscr = curses.initscr() # required

curses.noecho() # don't show keyboard input
curses.cbreak() # don't require enter to send input

stdscr.keypad(True)

def main(stdscr):
   # curses.newwin(5, 10, 7, 20)
   stdscr.addstr("SUMMON SHOGGOTHS")
   stdscr.addstr(20, 30, "Razzmatazz")
   stdscr.refresh()
   time.sleep(3)



wrapper(main)

# Unwind
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()

Pretty much everything happens that I would expect: Shoggoths are Summoned and Razzes are matazzed, however when I enter git status my lines breaks are broken.

Doing a diff between stty -a before and after showed:

5c5
< iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -iutf8
---
> iflags: -istrip -icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -iutf8
7c7
< oflags: opost onlcr -oxtabs -onocr -onlret
---
> oflags: opost -onlcr -oxtabs -onocr -onlret

After investigating these options, I found that issuing stty onlcr fixes the terminal. I'm surprised, however, because I thought that curses.endwin() would have reset me:

De-initialize the library, and return terminal to normal status.

I thought it might be an issue in iTerm2, so then I tried with Terminal.app. This produced the same behavior.

I'm stumped is there some other re-set technique? I saw in C-based implementations the stty data are often saved into a struct for restoration...that might be an avenue of pursuit.

Thanks for any help!


Solution

  • The likely problem is this (see source code):

    • the curses wrapper method does an initscr and endwin.
    • when your script exits from wrapper, the curses library is back in shell mode
    • changing curses.nocbreak() is redundant, and may be causing the curses library's current terminal modes to be updated from the program mode
    • calling endwin updates the wrong settings.

    I'd simply delete the chunk marked "# Unwind".