Python curses getch() always returns -1 if data is piped to my program

Viewed 185

I want to read data piped into my program and then take a user input using curses. I have the following code:

import curses
import sys
import select

def read_piped_data():
    data = ""
    while(sys.stdin in select.select([sys.stdin,],[],[],0.0)[0]):
        x = sys.stdin.read(1)
        if(len(x) == 0): break
        data += x
    sys.stdin = open("/dev/tty")
    return (data if len(data) > 0 else None)

def main(stdscr):
    data = read_piped_data()
    if(data != None): do_something1(data)
    curses.raw()
    stdscr.keypad(True)
    stdscr.timeout(0)

    while(True):
        ch = stdscr.getch()
        if(ch == -1): continue
        if(not do_something2(stdscr,ch)): break

curses.wrapper(main)

When I execute the above using:

$ python3 test.py

Everything works perfectly: do_something1() is not called but do_something2() is called whenever I press any key. But when I pipe something as:

$ ls | python3 test.py

My code fails: do_something1() is called and it correctly processes the piped-data but do_something2() is never called and my program is basically in an infinite loop, not registering any key-presses!

Keeping or removing the sys.stdin = open("/dev/tty") has no effect. In another stackoverflow answer I found this solution to make input() work (after reading from pipe). Although input() works after I reassign stdin, getch() doesn't.

Even if I do not read the piped data, just piping some command to my program is enough to mess with curses getch() preventing it from ever reading any key presses.

I am using curses with Python 3.8 on Ubuntu 20.04. How can I "reactivate" input in curses after having read from pipe?

1 Answers

Maybe because stdin is not guaranteed to be assignable? At C, I have no problem with this:

freopen("/dev/tty","r",stdin);

And reassigning with fopen is also working here.

Related