multiline input in terminal

I think this should be moved to Python Help.

Also I’m not aware of a difference between typing in the terminal

A
B
C

or pasting into the terminal

A
B
C
1 Like

True, here’s code that tries to reads everything you paste:

from sys import stdin
from termios import ECHO, ICANON, TCSANOW, VMIN, VTIME, tcgetattr, tcsetattr
from time import sleep

LFLAG = 3
CC = 6

mode = tcgetattr(stdin)
mode[LFLAG] &= ~(ECHO | ICANON)
mode[CC][VMIN] = 0
mode[CC][VTIME] = 0
tcsetattr(stdin, TCSANOW, mode)

while True:
    while not (text := stdin.read()):
        sleep(0.001) # don't block CPU

    print(repr(text))

This works in macOS’s Terminal:

'Alfa\nBravo\nCharlie\nDelta\nEcho\nFoxtrot\nGolf\nHotel\nIndia\nJuliett\nKilo\nLima\nMike\nNovember\nOscar\nPapa\nQuebec\nRomeo\nSierra\nTango\nUniform\nVictor\nWhiskey\nXray\nYankee\nZulu'

But not in VS Code’s terminal:

'Alfa\nBravo\nCharlie\nDelta\nEcho\nFoxtrot\nGolf\nHotel\nI'
'ndia\nJuliett\nKilo\nLima\nMike\nNovember\nOscar\nPapa\nQu'
'ebec\nRomeo\nSierra\nTango\nUniform\nVictor\nWhiskey\nXra'
'y\nYankee\nZulu'

So, if you’re doing this, you better wait for a bit:

from select import select
from sys import stdin
from termios import TCSANOW
from tty import setcbreak

setcbreak(stdin, TCSANOW)

while True:
    text = stdin.buffer.raw.read(1)
    while select([stdin.buffer], [], [], 0.01)[0]:
        text += stdin.buffer.raw.read(1)

    print(repr(text.decode()))

This isn’t super reliable, so I would use bracketed paste.

1 Like

“All in one go” will still be very slow for a computer.

The input function is meant for interactive use, I’m assuming you want the longinput function to also be usable for this. Such a function would need to have some way to know when the multiline input is done and AFAIK there is no generally accepted convention for that on unix-y systems other than specifying a sentinel value (such “type multiple lines, end with a blank line”), or reading until EOF when you need only one multi-line input.

There are conventions like using Ctrl-Enter to submit a multi-line value. That’s arguably just another form of sentinel, but in use it feels more like having a different keypress to say “submit”.

But nevertheless, there’s no “one size fits all” solution here. And that means there’s not much chance of getting something added to the stdlib. But the prompt-toolkit library offers powerful features for terminal IO, including multi-line input. I suggest the OP checks it out, to see if it meets their needs.

2 Likes

im thinking of copying and pasting into the terminal. just to be clear though, it shouldnt matter if the text is pasted. just thats if it’s multiline it should be readable by a single longinput as is

I was thinking when you hit the enter key, that’s whe Python would know you’re inputting the text.

I was just trying to explain to everyone that I’m pasting 100s of lines of text into my terminal at once, that doesn’t mean every multiline input is copy and pasted or has to be. I could grueling manually type those 100s of lines.

Well, the way input works is that you press ‘Enter’ and it knows you’ve stopped typing. That’s what I’d do.

Thank you for this resource, I’m using tkinter for this as of now. Regardless, Ctrl + Enter sounds fine. Frankly, you could make the current input() function understand that Enter means single line and Ctrl + Enter means multiline, if you don’t want a separate longinput function.

How is it supposed to distinguish between the enter key marking the end of one of the lines of the multiline input, and the enter key marking the end of the whole multiline input?

3 Likes

But you can’t tell the difference between Enter and Ctrl+Enter when reading from a terminal. They both read as a newline character.

Shift + Enter?

I am not entirely sure of the character input codes on all systems but it’s a triviality

It absolutely isn’t a triviality. There is no cross-platform way of detecting shift-enter, ctrl-enter, or any other reasonable “submit this multi-line input” keypress. It has to be done in platform-specific code, which is exactly what prompt-toolkit has done for you. Duplicating that effort, across all platforms (including iOS and WASM) that Python supports, would be a lot of work for the core devs, for something that’s rarely needed (based on the number of times anyone has asked for it) and available already at the cost of nothing more than a dependency on a PyPI module.

3 Likes

then youd have to be using the argument method of multiline(‘how are you’, ‘INPUTEND’) where until INPUTEND is detected on its own in a line, python knows the enter is just to get to the next line. if someone has a better system, or keymap idea, go ahead

def multiline(prompt, end):
    lines = [input(prompt)]
    while lines[-1] != end:
        lines.append(input(""))
    return "\n".join(lines[:-1])

Modify as you prefer to make it work the way you want it to.

4 Likes

That works very well, thank you Paul. While it isn’t as useful as Python letting you edit the text on previous lines (like a text editor), and then pressing some keymapping to input it, it’s still very nice and works great. Would you be opposed to including it somewhere, in a library or something?

I certainly have no intention of doing so myself. It’s so simple that you should just copy it into your project when you need it. I don’t think it’s complex enough for me to claim that I “own” it, so do what you want with it.

I don’t honestly understand why people aren’t willing to just write simple functions like this for themselves. Not everything needs to be in a library, and sometimes people should just maintain the code themselves rather than expect someone else to do so for them.

4 Likes