0

I am trying to write a Python program, which show/edit text from stdin using curses. I cloud implement edit module, but I couldn't stdin as input.

How can I solve this problem?

import curses

def main(stdscr):
    while key := stdscr.getkey():
        stdscr.addstr(0, 0, key)

if __name__ == '__main__':
    curses.wrapper(main)
echo "hello" | python edit.py

Traceback (most recent call last):
  File "/path/to/edit.py", line 8, in <module>
    curses.wrapper(main)
  File "/path/to/.pyenv/versions/3.9.1/lib/python3.9/curses/__init__.py", line 94, in wrapper
    return func(stdscr, *args, **kwds)
  File "/path/to/edit.py", line 4, in main
    while key := stdscr.getkey():
_curses.error: no input

And python edit.py (without echo "hello") is successfully complete.

ktrueda
  • 11
  • 3

1 Answers1

0

First you must read your data from stdin and not from curses.getkey() since you piped them.

So you can do this to read the stdin first then init and then display the content in curses :

import sys
import curses

stdin_content = ()

def main(stdscr):
    stdscr.clear() # and other curses init...
    while True:
        for i, line in enumerate(stdin_content):
            stdscr.addstr(2 + i, 2, "> " + line)
        stdscr.refresh()
        key = stdscr.getch()
        stdscr.timeout(1000)
        if key == ord('q'):
            break

if __name__ == '__main__':
    stdin_content = sys.stdin.readlines()
    curses.wrapper(main)

You can launch:

$ echo "hello" | python edit.py

And everything is fine, you show the line or the lines (if you cat something with multiple lines) you pass to your script in ncurses.

BUT there is this issue where getch() is not working if you read stdin before, so you won't be able to quit with 'q' even tho it's supposed to do so.

This subject has been answered by @Frédéric Hamidi here : Linux: Pipe into Python (ncurses) script, stdin and termios Read his answer for further detail but basically you have to dup the stdin.

Add this line right after reading stdin in main :

os.dup2(3, 0)

And instead of calling like this:

$ echo "hello" | python edit.py

Call like this:

$ (echo "hello" | python edit.py) 3<&0

Or alternatively spawn the subshell before like this :

$ exec 3<&0  

And then call normally

$ echo "hello" | python edit.py
$ cat test.txt | python edit.py

You can read the answers linkeda and the docs for further understanding. Hope this helps anyway.

darokin
  • 21
  • 4