-
Notifications
You must be signed in to change notification settings - Fork 725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Broken when piped to. #502
Comments
Hi @Granitosaurus, Thanks for reporting the issue. This is however something which won't be fixed. (I guess I definitely need to given better error message as feedback.) The reason is that prompt_toolkit is meant for user interaction, not for machine-interaction like a pipe. If you want to support pipe input on the other hand, it's best to test for The same is true for piping the output to something else. If you'd pipe stdout to a file, the result will be a meaningless sequence of ANSI escape characters. If |
@jonathanslenders is there a technical reason behind this? There seems to be a bit of weirdness going on with stdin/out when piping is involved. |
Yes, a Posix pipe is not the same as a PTY (pseudo terminal). Both are I/O devices, but they have different properties.
Probably there are other differences. There is a lot of non-obvious low level I/O underneath. But in any case, prompt_toolkit won't support pipes as stdin. Maybe with some hacks it will work partly, but seriously, it'll take only a few lines of code to read stdin yourself, if the input doesn't appear to be a tty. if not sys.stdin.isatty():
data = sys.stdin.read()
... |
Apologies for replying on an old thread, but I spent the better part of the day figuring out a workaround for this. And when I say workaround - I definitely mean hack. If you want to start a command by piping in input and then have an interactive prompt appear, e.g. '''data-producer | data-processor''' - where data-processor would then have an interactive prompt (I'm actually using PyInquirer to create a few checkbox interfaces, and it uses prompt-toolkit) I found that if you take the data from stdin before the creation of the prompt-toolkit application, you can do the following:
Now I'm sure this would create other issues for other applications and might not even work in all environments, but it worked for my purposes. I had tried setting Totally understand this isn't the intended use, but there are a few folks that seem to be trying to do something similar so if it's helpful, great. Also feel free to tell me why this is a terrible idea. I'm sure there's a reason why it hasn't been suggested. |
Adding my "me too" to this. It's sometimes very useful to be able to write a script which uses stdout/stderr for data, but also presents a user interface, so it would be great if there were a flag which caused prompt-toolkit to use /dev/tty for the UI. This is similar to the way eg "whiptail" produces a result on "stdout". |
Hi @ciphergoth, That's interesting. I didn't knew that "whiptail" was doing that. The way it works is as follows. Normally /dev/stdin is the input device and /dev/stdout is the output device. The file descriptors correspond to 0 and 1 respectively. However, when a program is attached to a TTY, /dev/stdin and /dev/stdout will refer to the same device - the slave side of the TTY - even though the file descriptors remain 0 and 1. This means, we can actually also open FD 1 for reading or FD 0 for writing. It is what every pager does. I discovered this actually when writing pypager, a $PAGER implementation using prompt_toolkit. The relevant line is the following where we pass sys.stdout to the create_input function: https://github.com/prompt-toolkit/pypager/blob/master/pypager/pager.py#L121 So, here's a question to all of you:
I know some people try to pipe data into the stdin, and they expect prompt_toolkit to handle that (even though it's not always working perfect). So, I'm not sure what's the best default. |
I am not sure that this should be the only option, but there should be a documented way to get this behavior. As people have said, attempts to do this by hand result in a bad file descriptor error. My application is that I want a script that helps me set up environment variables, so I have function set_vars { source <( ./ask_for_vars ) } and this doesn't work because stdout is redirected. |
@ciphergoth, I'm tempted to say that it's a reasonable choice to switch the output to /dev/stderr automatically if stdout is not a tty, but stderr is. The output that prompt_toolkit generates is not really meant for consumption by anything other then a terminal emulator. @asmeurer: What are your opinions on this matter? You have been scripting prompt_toolkit with pexect, and trying to pipe data into a prompt_toolkit application. |
Just want to add another modern real life example - fzf. As we know:
|
JFYI I got answer from fzf owner:
|
Came to this issue having the same issue as #1943 I used to have code like: # if stdin is not a tty, we might be getting piped input, which we should include in the prompt
was_piped = False
if not sys.stdin.isatty():
# fetch prompt from stdin
prompt_stdin = _read_stdin()
if prompt_stdin:
initial_msgs += [Message("system", f"```stdin\n{prompt_stdin}\n```")]
was_piped = True
# Attempt to switch to interactive mode
sys.stdin.close()
try:
sys.stdin = open("/dev/tty")
except OSError:
# if we can't open /dev/tty, we're probably in a CI environment, so we should just continue
logger.warning(
"Failed to switch to interactive mode, continuing in non-interactive mode"
) But after switching from rich+readline to prompt-toolkit, I got the same issue as #1943. The workaround by @chrisgoddard (#502 (comment)) seems to have worked: ErikBjare/gptme@b3974c1 Would appreciate knowledge about any known footguns with this workaround. |
When piping to python script that uses
prompt()
everything breaks:To reproduce:
Then:
The text was updated successfully, but these errors were encountered: