-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
io/os: Implement IsTerminal trait on Stdio #91121
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @m-ou-se (or someone else) soon. Please see the contribution instructions for more information. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was passing by so I thought I would give my impression of your PR.
PS: I'm not a Rust reviewer or maintainer, just a random guy. 😄
@@ -1202,6 +1202,59 @@ where | |||
} | |||
} | |||
|
|||
/// Trait to determine if stdio stream (Stdin, Stdout, Stderr) is a terminal/tty. | |||
#[unstable(feature = "is_terminal", issue = "80937")] | |||
pub trait IsTerminal { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you using a trait ? Are you expecting that user code will want to implement that trait ? (if not it must be sealed with a private module).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a scenario in which user code would need to implement this trait, so it could just be converted to regular impl
s if that is easier. I originally created it as a trait after a recommendation from @bjorn3: #81807 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think this should start out as a sealed trait. I can imagine reasons why people might want to implement it in the future, but we should be able to make the decision about exposing the trait and its method independently of the decision about letting people implement it outside the standard library.
#[unstable(feature = "is_terminal", issue = "80937")] | ||
pub trait IsTerminal { | ||
/// returns true if stdio stream (Stdin, Stdout, Stderr) is a terminal/tty. | ||
fn is_terminal() -> bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should maybe return an Option
of bool
where None
will be returned when we are unable to determine if it's either a terminal or not.
fn is_terminal() -> bool; | |
fn is_terminal() -> Option<bool>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a solution to the Windows hack problem. Instead of falling back to the msys_tty_on
hack, we just return None? Although I'm not sure how useful this would be to the end user, as I'm assuming that any None
s would have to be treated the same as Some(false)
in the calling code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be appropriate to return io::Result<bool>
here. If we get the expected error that indicates "not a terminal" we should return false
, but if we get an unexpected error we should return that error
#[unstable(feature = "is_terminal", issue = "80937")] | ||
impl io::IsTerminal for sys::stdio::Stdin { | ||
fn is_terminal() -> bool { | ||
unsafe { libc::isatty(libc::STDIN_FILENO) != 0 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this return true in case of an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The man page indicates that this function can only return 0 or 1: https://linux.die.net/man/3/isatty
I could make it libc::isatty(libc::STDIN_FILENO) == 1
to be more explicit?
I'm not sure I understand the intent of the Windows code. It returns I'm also somewhat concerned about having the msys hack in the standard library as it looks like it's purely based on the file name. Though I'm not sure how much of a problem this will be in practice. |
I think the intent is that if any handle is a console handle, then we are running inside a non-msys terminal, so if say stdout is not a console handle, then we know for a fact that it is redirected. If all handles are not console handles, then we may be running inside an msys terminal, so we need to use the filename hack to check if this is the case. Stdin and stdout and stderr are rarely redirected at the same time, right? So the filename hack should rarely be used. |
Ah, thanks I get it now. I guess what I'm struggling with is how reliable these heuristics are and in what situations they're needed. |
For prior art, git uses |
The code for this is basically lifted from the I'm not an expert on the ins and outs of Windows terminals unfortunately, so i'll have to defer to those who know better. @BurntSushi was the original author of the Windows logic, and explains the reasoning here #81807 (comment). He also mentions how I suppose the biggest issue is if we are OK with having a heuristic-y detection inside the standard library. But considering |
Right. I got the logic straight from git. Folks are right to be skeptical of it. It's quite precarious looking, and it certainly is not bullet proof. But, it works well enough in practice. I do think it's probably tied to whether we expose this sort of functionality at all in the first place. If we expose something that doesn't work in some particularly common cases on Windows, then I think that just wouldn't be good because probably folks wouldn't use it or have to invent their own work-arounds. |
Huh, I guess the git code has changed slightly. Or I'm looking at something different. Side note: I've been testing this a bit. It seems that the version of msys used by "Git Bash" on Windows uses winpty so that non-msys applications run as expected (or at least on my install of it does). This confused me at first because I couldn't initially reproduce the issue until I switched to a clean install of msys2. |
Oh interesting. I remember learning about the high level logic from git and
porting it. But that was some number of years ago. I'll try to give a look
later and see if I can clarify.
…On Mon, Nov 22, 2021, 10:27 Chris Denton ***@***.***> wrote:
He also mentions how git does it.
Huh, I guess the git code has changed slightly. Or I'm looking at
something different.
Side note: I've been testing this a bit. It seems that the version of msys
used by "Git Bash" on Windows uses winpty
<https://github.com/rprichard/winpty> so that non-msys applications run
as expected (or at least on my install of it does). This confused me at
first because I couldn't initially reproduce the issue until I switched to
a clean install of msys2.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#91121 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADPPYWTTPR36IITHIDYLIDUNJOP7ANCNFSM5IP5RDQA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
No worries. The core part of testing the name is the same in any case. I'm trying to see if there is any precedent in other language standard libraries to see if they are of any help. |
If it helps, I don't think my reservations should be a blocker to adding this unstably. It could be mentioned as a concern in the tracking issue or the msys heuristics could be put in a separate PR and discussed independently. I'd personally be ok with either. |
r? @Amanieu |
#[unstable(feature = "is_terminal", issue = "80937")] | ||
pub trait IsTerminal { | ||
/// returns true if stdio stream (Stdin, Stdout, Stderr) is a terminal/tty. | ||
fn is_terminal() -> bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn is_terminal() -> bool; | |
fn is_terminal() -> io::Result<bool>; |
This would allow for implementations that might return errors when attempting to determine this.
I do think this trait needs to be sealed before we add it. I also personally think it should return an In the future, I'd love to see implementations of this trait for |
|
||
#[unstable(feature = "is_terminal", issue = "80937")] | ||
impl io::IsTerminal for sys::stdio::Stdin { | ||
fn is_terminal() -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of duplication here, this should be factored out into a method that takes fd
and others
as arguments.
@@ -118,3 +118,23 @@ pub fn is_ebadf(_err: &io::Error) -> bool { | |||
pub fn panic_output() -> Option<impl io::Write> { | |||
Some(Stderr::new()) | |||
} | |||
|
|||
#[unstable(feature = "is_terminal", issue = "80937")] | |||
impl io::IsTerminal for sys::stdio::Stdin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The types in sys
are private, so there is no need to use a trait here. These can just be inherent methods on the private type.
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
Closing this due to inactivity |
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
… r=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…=thomcc Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <[email protected]>
…rminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang/rust#91121 Co-authored-by: Matt Wilkinson <[email protected]>
Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang/rust#91121 Co-authored-by: Matt Wilkinson <[email protected]>
Supersedes #81807
Fixes #80937
I've reimplemented the changes off of the latest
master
as the rebase on the old PR was causing issues.This PR adds a
IsTerminal
trait toStdin
,Stdout
, andStderr
for the Unix, Windows, and Hermit platforms. This allows the stdlib's CLI tests to make use of this trait instead of a customisatty
implementation contained only within the testing crate.I also incorporated @r00ster91's feedback from the original PR on using
STD_INPUT_HANDLE
and such directly instead of importing and renaming them.