Rust: Please provide a portable isatty function for stdin/stdout/stderr

Created on 19 May 2016  路  17Comments  路  Source: rust-lang/rust

Some programs want to automatically emit color output, or spawn a pager, if output goes to a terminal, but not with redirected output. libtest has a function stdout_isatty, and rustc's libsyntax has a function stderr_isatty, with almost identical copy/pasted code other than the fd/handle checked. This strongly suggests the need for a library implementation.

Please consider providing a portable implementation of "isatty" for the Stdin, Stdout, and Stderr types, based on the above implementations from rustc/libtest.

C-feature-request T-libs

Most helpful comment

@retep998 There are other reasons to want to know if stdout or stderr is a tty, besides formatting.

All 17 comments

There's an atty crate on crates.io. It doesn't seem to allow querying anything other than stdout at the moment.

@birkenfeld It also doesn't seem to work correctly on Windows: https://github.com/softprops/atty/issues/3

I think it would make sense to add the code rustc is using to an appropriate library, to avoid duplicating it. Perhaps it would make sense to have a trait for this, and impl that trait for the Stdin, Stdout, and Stderr types?

(If this needs to go through an RFC rather than an issue filed against Rust, I can file one.)

Right now libstd does not provide a way to get the HANDLE of stdout, so even if you could ask libstd whether it is a tty, you wouldn't be able to actually do any console shenanigans with it on Windows. If you call GetStdHandle to get stdout, now you're bypassing libstd's stdout, which means when output is thread locally redirected during testing, you'll bypass that redirection. Furthermore there isn't really a good way to tell whether a pipe is an msys/mintty pipe which can accept ansi escape codes, aside from getting the name of the pipe and guessing based on that.

For rustc we won鈥檛 have any problems with using external libraries (i.e. the atty crate) once we move to cargo-based build system. This is also somewhat against rust鈥檚 tendency to keep libstd small.

The atty crate not supporting windows is trivially fixed by porting the relevant code from rustc to there. Well, other than the HANDLE thing, which we should eventually expose.

@retep998

Right now libstd does not provide a way to get the HANDLE of stdout, so even if you could ask libstd whether it is a tty, you wouldn't be able to actually do any console shenanigans with it on Windows. If you call GetStdHandle to get stdout, now you're bypassing libstd's stdout, which means when output is thread locally redirected during testing, you'll bypass that redirection.

By "thread-local redirection", do you mean changing what std::io::stdout() refers to _without_ changing the process's fd 1 (or Windows equivalent)? If Rust provides such a mechanism, then sure, supporting that seems useful.

Furthermore there isn't really a good way to tell whether a pipe is an msys/mintty pipe which can accept ansi escape codes, aside from calling an unstable NT function to get the name of the pipe and guessing based on that.

I don't think we want to return "true" for _any_ kind of pipe. isatty just says if output is going to a terminal; if the caller wants to determine if they should send escape sequences to a pipe, that's a separate problem (e.g. --color=always, or a self-spawned pager known to support color like less -R).

By "thread-local redirection", do you mean changing what std::io::stdout() refers to without changing the process's fd 1 (or Windows equivalent)? If Rust provides such a mechanism, then sure, supporting that seems useful.

In order to support libtest, which captures the output of tests so they don't get printed out, libstd does have its own internal thread local redirection. When you write to io::stdout() or call println! you are going through that abstraction. Also, because Rust caches the result of calling GetStdHandle, if something calls SetStdHandle then that will result in a disparity between io::stdout() and GetStdHandle as well.

I don't think we want to return "true" for any kind of pipe.

If you want to completely ignore all terminals that aren't the standard Windows console, sure. While I personally don't have a problem with throwing mintty under the bus, there are probably other people who do care.

Also note that since stdout and stderr can be redirected independently, you'd have to specify whether it is stdout or stderr to isatty. The atty crate doesn't provide an option for this.

If you want to completely ignore all terminals that aren't the standard Windows console, sure. While I personally don't have a problem with throwing mintty under the bus, there are probably other people who do care.

Short of programs providing an option to override the detection (e.g. --color=always or an equivalent via environment or config file), I don't see any obvious way around that. If the fd or HANDLE looks like a pipe, how can the program tell the difference between a pipe to a terminal that handles escape sequences, and a pipe to a program that doesn't?

In the meantime, I factored Cargo's implementation into the isatty crate because I needed it for cargo-expand.

@dtolnay Thanks!

@retep998 Do you have a pointer to the thread-local redirection mechanism in libstd? I can't seem to find anything about it, except some hints that such a mechanism existed in old_io.

@retep998 Thanks. Unstable, with documentation hidden; no wonder I didn't find it.

The https://github.com/softprops/atty crate now also does this. I'm somewhat uncertain that we should implement such a function in libstd, but wouldn't be against it either.

I agree that isatty information seems appropriate to provide for the standard library Stdin Stdout Stderr types. I would be interested in seeing a concrete design proposed in an RFC if someone is interested in spearheading that.

Unless libstd wants to tackle the task of providing TTY specific functionality such as colors and stuff using ANSI escape codes and windows console api calls, then there is very little reason for libstd to provide isatty.

If someone would be interested in proposing an RFC, I'd be happy to mentor them through the process.

@retep998 There are other reasons to want to know if stdout or stderr is a tty, besides formatting.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GuillaumeGomez picture GuillaumeGomez  路  300Comments

nikomatsakis picture nikomatsakis  路  259Comments

nikomatsakis picture nikomatsakis  路  236Comments

withoutboats picture withoutboats  路  202Comments

Leo1003 picture Leo1003  路  898Comments