diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c116fe30e..480fba54a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ _Unreleased_ - Updated to `uv` 0.1.6. #850 +- Trap panics and silence bad pipe errors. #862 + ## 0.28.0 Released on 2024-03-07 diff --git a/rye/src/main.rs b/rye/src/main.rs index 7976506914..0a4fb23ee2 100644 --- a/rye/src/main.rs +++ b/rye/src/main.rs @@ -1,6 +1,6 @@ -use std::process; use std::sync::atomic::{AtomicBool, Ordering}; +use crate::utils::panic::trap_bad_pipe; use crate::utils::QuietExit; #[macro_use] @@ -34,6 +34,8 @@ pub fn disable_ctrlc_handler() { } pub fn main() { + crate::utils::panic::set_panic_hook(); + ctrlc::set_handler(move || { if !DISABLE_CTRLC_HANDLER.load(Ordering::Relaxed) { let term = console::Term::stderr(); @@ -48,26 +50,27 @@ pub fn main() { }) .unwrap(); - let result = cli::execute(); - let status = match result { - Ok(()) => 0, - Err(err) => { - if let Some(err) = err.downcast_ref::() { - err.print().ok(); - err.exit_code() - } else if let Some(QuietExit(code)) = err.downcast_ref() { - *code - } else { - error!("{:?}", err); - 1 + trap_bad_pipe(|| { + let result = cli::execute(); + let status = match result { + Ok(()) => 0, + Err(err) => { + if let Some(err) = err.downcast_ref::() { + err.print().ok(); + err.exit_code() + } else if let Some(QuietExit(code)) = err.downcast_ref() { + *code + } else { + error!("{:?}", err); + 1 + } } - } - }; + }; - if SHOW_CONTINUE_PROMPT.load(Ordering::Relaxed) { - echo!("Press any key to continue"); - console::Term::buffered_stderr().read_key().ok(); - } - - process::exit(status); + if SHOW_CONTINUE_PROMPT.load(Ordering::Relaxed) { + echo!("Press any key to continue"); + console::Term::buffered_stderr().read_key().ok(); + } + status + }); } diff --git a/rye/src/utils/mod.rs b/rye/src/utils/mod.rs index 7a9f7fbe32..9ddf4f5624 100644 --- a/rye/src/utils/mod.rs +++ b/rye/src/utils/mod.rs @@ -34,6 +34,7 @@ pub(crate) mod windows; #[cfg(unix)] pub(crate) mod unix; +pub(crate) mod panic; pub(crate) mod ruff; pub(crate) mod toml; diff --git a/rye/src/utils/panic.rs b/rye/src/utils/panic.rs new file mode 100644 index 0000000000..b4e724d806 --- /dev/null +++ b/rye/src/utils/panic.rs @@ -0,0 +1,32 @@ +use std::any::Any; +use std::{panic, process}; + +fn is_bad_pipe(payload: &dyn Any) -> bool { + payload + .downcast_ref::() + .map_or(false, |x| x.contains("failed printing to stdout: ")) +} + +/// Registers a panic hook that hides stdout printing failures. +pub fn set_panic_hook() { + let default_hook = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + if !is_bad_pipe(info.payload()) { + default_hook(info) + } + })); +} + +/// Catches down panics that are caused by bad pipe errors. +pub fn trap_bad_pipe i32 + Send + Sync>(f: F) -> ! { + process::exit(match panic::catch_unwind(panic::AssertUnwindSafe(f)) { + Ok(status) => status, + Err(panic) => { + if is_bad_pipe(&panic) { + 1 + } else { + panic::resume_unwind(panic); + } + } + }); +}