diff --git a/Cargo.lock b/Cargo.lock index 26bf8aef8..96a4c2142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,13 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "anyhow" +version = "0.1.0" +dependencies = [ + "anyhow 1.0.98", +] + [[package]] name = "anyhow" version = "1.0.98" @@ -509,7 +516,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527f6e2a4e80492e90628052be879a5996c2453ad5ec745bfa310a80b7eca20a" dependencies = [ - "anyhow", + "anyhow 1.0.98", "core-foundation", "filetime", "hex", @@ -3113,7 +3120,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" dependencies = [ - "anyhow", + "anyhow 1.0.98", "async-trait", "http", "reqwest", @@ -3128,7 +3135,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178" dependencies = [ - "anyhow", + "anyhow 1.0.98", "async-trait", "futures", "getrandom 0.2.15", @@ -4297,7 +4304,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382e025ef8e0db646343dd2cf56af9d7fe6f5eabce5f388f8e5ec7234f555a0f" dependencies = [ - "anyhow", + "anyhow 1.0.98", "fs-err 2.11.0", "itertools 0.13.0", "once_cell", @@ -4384,6 +4391,22 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "traversable-derive" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "traversable-error" +version = "0.1.0" +dependencies = [ + "anyhow 0.1.0", + "traversable-derive", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4575,7 +4598,7 @@ name = "uv" version = "0.7.2" dependencies = [ "anstream", - "anyhow", + "anyhow 0.1.0", "assert_cmd", "assert_fs", "axoupdater", @@ -4621,6 +4644,7 @@ dependencies = [ "tracing-durations-export", "tracing-subscriber", "tracing-tree", + "traversable-error", "unicode-width 0.2.0", "url", "uv-auth", @@ -4678,7 +4702,7 @@ dependencies = [ name = "uv-auth" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "async-trait", "base64 0.22.1", "futures", @@ -4708,7 +4732,7 @@ dependencies = [ name = "uv-bench" version = "0.0.0" dependencies = [ - "anyhow", + "anyhow 0.1.0", "codspeed-criterion-compat", "criterion", "jiff", @@ -4736,7 +4760,7 @@ dependencies = [ name = "uv-build" version = "0.7.2" dependencies = [ - "anyhow", + "anyhow 0.1.0", "uv-build-backend", "uv-version", ] @@ -4761,6 +4785,7 @@ dependencies = [ "thiserror 2.0.12", "toml", "tracing", + "traversable-error", "uv-distribution-filename", "uv-fs", "uv-globfilter", @@ -4797,6 +4822,7 @@ dependencies = [ "tokio", "toml_edit", "tracing", + "traversable-error", "uv-configuration", "uv-distribution", "uv-distribution-types", @@ -4849,6 +4875,7 @@ dependencies = [ "thiserror 2.0.12", "toml", "tracing", + "traversable-error", "walkdir", ] @@ -4868,7 +4895,7 @@ name = "uv-cli" version = "0.0.1" dependencies = [ "anstream", - "anyhow", + "anyhow 0.1.0", "clap", "clap_complete_command", "fs-err 3.1.0", @@ -4895,7 +4922,7 @@ dependencies = [ name = "uv-client" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "async-trait", "async_http_range_reader", "async_zip", @@ -4925,6 +4952,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "traversable-error", "url", "uv-auth", "uv-cache", @@ -4950,7 +4978,7 @@ dependencies = [ name = "uv-configuration" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "clap", "either", "fs-err 3.1.0", @@ -4963,6 +4991,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tracing", + "traversable-error", "url", "uv-auth", "uv-cache", @@ -4989,7 +5018,7 @@ name = "uv-dev" version = "0.0.1" dependencies = [ "anstream", - "anyhow", + "anyhow 0.1.0", "clap", "fs-err 3.1.0", "itertools 0.14.0", @@ -5043,13 +5072,14 @@ dependencies = [ name = "uv-dispatch" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "futures", "itertools 0.14.0", "rustc-hash", "thiserror 2.0.12", "tokio", "tracing", + "traversable-error", "uv-build-backend", "uv-build-frontend", "uv-cache", @@ -5074,7 +5104,7 @@ dependencies = [ name = "uv-distribution" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "either", "fs-err 3.1.0", "futures", @@ -5093,6 +5123,7 @@ dependencies = [ "tokio-util", "toml", "tracing", + "traversable-error", "url", "uv-cache", "uv-cache-info", @@ -5127,6 +5158,7 @@ dependencies = [ "serde", "smallvec", "thiserror 2.0.12", + "traversable-error", "url", "uv-cache-key", "uv-normalize", @@ -5156,6 +5188,7 @@ dependencies = [ "thiserror 2.0.12", "toml", "tracing", + "traversable-error", "url", "uv-auth", "uv-cache-info", @@ -5191,6 +5224,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "traversable-error", "uv-configuration", "uv-distribution-filename", "uv-pypi-types", @@ -5225,7 +5259,8 @@ dependencies = [ name = "uv-git" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", + "anyhow 1.0.98", "cargo-util", "dashmap", "fs-err 3.1.0", @@ -5234,6 +5269,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tracing", + "traversable-error", "url", "uv-auth", "uv-cache-key", @@ -5251,6 +5287,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "tracing", + "traversable-error", "url", ] @@ -5268,6 +5305,7 @@ dependencies = [ "tempfile", "thiserror 2.0.12", "tracing", + "traversable-error", "walkdir", ] @@ -5275,7 +5313,7 @@ dependencies = [ name = "uv-install-wheel" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "assert_fs", "clap", "configparser", @@ -5297,6 +5335,7 @@ dependencies = [ "tempfile", "thiserror 2.0.12", "tracing", + "traversable-error", "uv-cache-info", "uv-distribution-filename", "uv-fs", @@ -5313,7 +5352,7 @@ dependencies = [ name = "uv-installer" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "async-channel", "fs-err 3.1.0", "futures", @@ -5324,6 +5363,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tracing", + "traversable-error", "url", "uv-cache", "uv-cache-info", @@ -5367,6 +5407,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "traversable-error", "uv-distribution-filename", "uv-normalize", "uv-pypi-types", @@ -5430,6 +5471,7 @@ dependencies = [ "thiserror 2.0.12", "tracing", "tracing-test", + "traversable-error", "unicode-width 0.2.0", "url", "uv-fs", @@ -5456,6 +5498,7 @@ dependencies = [ "rustc-hash", "serde", "thiserror 2.0.12", + "traversable-error", "uv-small-str", ] @@ -5481,6 +5524,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "traversable-error", "url", "uv-auth", "uv-cache", @@ -5500,7 +5544,7 @@ dependencies = [ name = "uv-pypi-types" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "hashbrown 0.15.3", "indexmap", "insta", @@ -5517,6 +5561,7 @@ dependencies = [ "thiserror 2.0.12", "toml_edit", "tracing", + "traversable-error", "url", "uv-distribution-filename", "uv-git-types", @@ -5530,7 +5575,7 @@ dependencies = [ name = "uv-python" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "assert_fs", "clap", "configparser", @@ -5562,6 +5607,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "traversable-error", "url", "uv-cache", "uv-cache-info", @@ -5590,7 +5636,7 @@ dependencies = [ name = "uv-requirements" version = "0.1.0" dependencies = [ - "anyhow", + "anyhow 0.1.0", "configparser", "console", "fs-err 3.1.0", @@ -5600,6 +5646,7 @@ dependencies = [ "thiserror 2.0.12", "toml", "tracing", + "traversable-error", "url", "uv-cache-key", "uv-client", @@ -5624,7 +5671,7 @@ dependencies = [ name = "uv-requirements-txt" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "assert_fs", "fs-err 3.1.0", "indoc", @@ -5639,6 +5686,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tracing", + "traversable-error", "unscanny", "url", "uv-client", @@ -5681,6 +5729,7 @@ dependencies = [ "toml", "toml_edit", "tracing", + "traversable-error", "url", "uv-cache-key", "uv-client", @@ -5718,6 +5767,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "toml", + "traversable-error", "url", "uv-pep440", "uv-pep508", @@ -5738,6 +5788,7 @@ dependencies = [ "thiserror 2.0.12", "toml", "tracing", + "traversable-error", "url", "uv-cache-info", "uv-configuration", @@ -5761,7 +5812,7 @@ dependencies = [ name = "uv-shell" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "home", "same-file", "tracing", @@ -5810,6 +5861,7 @@ dependencies = [ "toml", "toml_edit", "tracing", + "traversable-error", "uv-cache", "uv-dirs", "uv-distribution-types", @@ -5837,6 +5889,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "tracing", + "traversable-error", "url", "uv-distribution-types", "uv-normalize", @@ -5849,11 +5902,12 @@ dependencies = [ name = "uv-trampoline-builder" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "assert_cmd", "assert_fs", "fs-err 3.1.0", "thiserror 2.0.12", + "traversable-error", "uv-fs", "which", "zip", @@ -5863,9 +5917,10 @@ dependencies = [ name = "uv-types" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "rustc-hash", "thiserror 2.0.12", + "traversable-error", "url", "uv-cache", "uv-configuration", @@ -5895,6 +5950,7 @@ dependencies = [ "self-replace", "thiserror 2.0.12", "tracing", + "traversable-error", "uv-fs", "uv-pypi-types", "uv-python", @@ -5915,7 +5971,7 @@ dependencies = [ name = "uv-workspace" version = "0.0.1" dependencies = [ - "anyhow", + "anyhow 0.1.0", "assert_fs", "fs-err 3.1.0", "glob", @@ -5932,6 +5988,7 @@ dependencies = [ "toml", "toml_edit", "tracing", + "traversable-error", "url", "uv-build-backend", "uv-cache-key", diff --git a/Cargo.toml b/Cargo.toml index 717c9250e..65422556b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ authors = ["uv"] license = "MIT OR Apache-2.0" [workspace.dependencies] +traversable-error = { path = "crates/traversable-error" } +traversable-derive = { path = "crates/traversable-derive" } uv-auth = { path = "crates/uv-auth" } uv-build-backend = { path = "crates/uv-build-backend" } uv-build-frontend = { path = "crates/uv-build-frontend" } @@ -72,7 +74,8 @@ uv-warnings = { path = "crates/uv-warnings" } uv-workspace = { path = "crates/uv-workspace" } anstream = { version = "0.6.15" } -anyhow = { version = "1.0.89" } +anyhow = { path = "crates/anyhow-wrapper" } +anyhow-original = { package = "anyhow", version = "1.0.89" } arcstr = { version = "1.2.0" } astral-tokio-tar = { version = "0.5.1" } async-channel = { version = "2.3.1" } diff --git a/crates/anyhow-wrapper/Cargo.toml b/crates/anyhow-wrapper/Cargo.toml new file mode 100644 index 000000000..cd9eb6792 --- /dev/null +++ b/crates/anyhow-wrapper/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "anyhow" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true +homepage.workspace = true +documentation.workspace = true +repository.workspace = true +authors.workspace = true +license.workspace = true + +[dependencies] +anyhow-original = { workspace = true } + +[lints] +workspace = true diff --git a/crates/anyhow-wrapper/src/lib.rs b/crates/anyhow-wrapper/src/lib.rs new file mode 100644 index 000000000..e942e5fc2 --- /dev/null +++ b/crates/anyhow-wrapper/src/lib.rs @@ -0,0 +1,174 @@ +// macro pass-through +pub use anyhow_original; +use std::convert::Infallible; + +#[macro_export] +macro_rules! anyhow { + ($($args:tt)*) => { + $crate::Error::Anyhow($crate::anyhow_original::anyhow!($($args)*)) + }; +} + +#[macro_export] +macro_rules! format_err { + ($($args:tt)*) => { + $crate::Error::Anyhow($crate::anyhow_original::format_err!($($args)*)) + }; +} + +#[macro_export] +macro_rules! bail { + ($msg:literal $(,)?) => { + return $crate::anyhow_original::__private::Err($crate::anyhow!($msg)) + }; + ($err:expr $(,)?) => { + return $crate::anyhow_original::__private::Err($crate::anyhow!($err)) + }; + ($fmt:expr, $($arg:tt)*) => { + return $crate::anyhow_original::__private::Err($crate::anyhow!($fmt, $($arg)*)) + }; +} + +/// Copied almost verbatim from anyhow +pub trait Context { + /// Wrap the error value with additional context. + fn context(self, context: C) -> anyhow_original::Result + where + C: std::fmt::Display + Send + Sync + 'static; + + /// Wrap the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context(self, f: F) -> anyhow_original::Result + where + C: std::fmt::Display + Send + Sync + 'static, + F: FnOnce() -> C; +} + +impl Context for std::result::Result +where + E: std::error::Error + Send + Sync + 'static, +{ + fn context(self, context: C) -> std::result::Result + where + C: std::fmt::Display + Send + Sync + 'static, + { + Ok(anyhow_original::Context::context(self, context)?) + } + + fn with_context(self, context: F) -> std::result::Result + where + C: std::fmt::Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + Ok(anyhow_original::Context::with_context(self, context)?) + } +} + +impl Context for Option { + fn context(self, context: C) -> std::result::Result + where + C: std::fmt::Display + Send + Sync + 'static, + { + Ok(anyhow_original::Context::context(self, context)?) + } + + fn with_context(self, context: F) -> std::result::Result + where + C: std::fmt::Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + Ok(anyhow_original::Context::with_context(self, context)?) + } +} + +pub type Result = std::result::Result; + +pub trait TraversableError: std::error::Error + Send + Sync + 'static { + fn name(&self) -> &str; +} + +pub enum Error { + Traversable(Box), + Anyhow(anyhow_original::Error), +} + +impl Error { + pub fn new(error: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + Self::Anyhow(anyhow_original::Error::new(error)) + } + + pub fn chain(&self) -> anyhow_original::Chain { + match self { + Error::Traversable(err) => anyhow_original::Chain::new(err.as_ref()), + Error::Anyhow(err) => err.chain(), + } + } +} + +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Traversable(err) => std::fmt::Debug::fmt(err, f), + Self::Anyhow(err) => std::fmt::Debug::fmt(err, f), + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Traversable(err) => std::fmt::Display::fmt(err, f), + Self::Anyhow(err) => std::fmt::Display::fmt(err, f), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Traversable(err) => Some(err.as_ref()), + Self::Anyhow(err) => Some(err.as_ref()), + } + } +} + +impl From for Error { + fn from(error: E) -> Self { + Self::Traversable(Box::new(error)) + } +} + +impl TraversableError for Box { + fn name(&self) -> &str { + (&**self).name() + } +} + +impl TraversableError for std::fmt::Error { + fn name(&self) -> &str { + "std::fmt::Error" + } +} + +impl TraversableError for std::io::Error { + fn name(&self) -> &str { + "std::io::Error" + } +} + +// TODO(konsti): Where are the matching std and anyhow methods? +// anyhow supports `GIT.as_ref()?` but our error doesn't. +impl TraversableError for &'static E { + fn name(&self) -> &str { + TraversableError::name(*self) + } +} + +impl From for Error { + fn from(error: anyhow_original::Error) -> Self { + Self::Anyhow(error) + } +} diff --git a/crates/traversable-derive/Cargo.toml b/crates/traversable-derive/Cargo.toml new file mode 100644 index 000000000..83a2c9083 --- /dev/null +++ b/crates/traversable-derive/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "traversable-derive" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true +homepage.workspace = true +documentation.workspace = true +repository.workspace = true +authors.workspace = true +license.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote = { workspace = true } +syn = { workspace = true } + +[lints] +workspace = true diff --git a/crates/traversable-derive/src/lib.rs b/crates/traversable-derive/src/lib.rs new file mode 100644 index 000000000..a74817851 --- /dev/null +++ b/crates/traversable-derive/src/lib.rs @@ -0,0 +1,51 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +#[proc_macro_derive(TraversableError)] +pub fn derive_traversable_error(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + let name_impl = match input.data { + Data::Enum(ref enum_data) => { + let match_arms = enum_data.variants.iter().map(|variant| { + let variant_name = &variant.ident; + let field_pattern = match &variant.fields { + Fields::Named(_) => quote! { { .. } }, + Fields::Unnamed(_) => quote! { (..) }, + Fields::Unit => quote! {}, + }; + + quote! { + #name::#variant_name #field_pattern => { + concat!(stringify!(#name), "::", stringify!(#variant_name)) + } + } + }); + + quote! { + fn name(&self) -> &str { + match self { + #(#match_arms,)* + } + } + } + } + _ => { + quote! { + fn name(&self) -> &str { + stringify!(#name) + } + } + } + }; + + let expanded = quote! { + impl ::traversable_error::anyhow::TraversableError for #name { + #name_impl + } + }; + + expanded.into() +} diff --git a/crates/traversable-error/Cargo.toml b/crates/traversable-error/Cargo.toml new file mode 100644 index 000000000..f929b88bd --- /dev/null +++ b/crates/traversable-error/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "traversable-error" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true +homepage.workspace = true +documentation.workspace = true +repository.workspace = true +authors.workspace = true +license.workspace = true + +[lib] + +[dependencies] +anyhow = { workspace = true} +traversable-derive = { workspace = true} + +[lints] +workspace = true diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs index cb9b2be20..dafd443a4 100644 --- a/crates/uv-auth/src/middleware.rs +++ b/crates/uv-auth/src/middleware.rs @@ -243,10 +243,13 @@ impl Middleware for AuthMiddleware { // // Clone the request so we can retry it on authentication failure let retry_request = request.try_clone().ok_or_else(|| { - Error::Middleware(anyhow!( - "Request object is not cloneable. Are you passing a streaming body?" - .to_string() - )) + Error::Middleware( + anyhow!( + "Request object is not cloneable. Are you passing a streaming body?" + .to_string() + ) + .into(), + ) })?; let response = next.clone().run(request, extensions).await?; @@ -336,9 +339,9 @@ impl Middleware for AuthMiddleware { if let Some(response) = response { Ok(response) } else { - Err(Error::Middleware(format_err!( - "Missing credentials for {url}" - ))) + Err(Error::Middleware( + format_err!("Missing credentials for {url}").into(), + )) } } } @@ -361,7 +364,9 @@ impl AuthMiddleware { }; let url = request.url().clone(); if matches!(auth_policy, AuthPolicy::Always) && credentials.password().is_none() { - return Err(Error::Middleware(format_err!("Missing password for {url}"))); + return Err(Error::Middleware( + format_err!("Missing password for {url}").into(), + )); } let result = next.run(request, extensions).await; diff --git a/crates/uv-build-backend/Cargo.toml b/crates/uv-build-backend/Cargo.toml index f23581662..c91e87dda 100644 --- a/crates/uv-build-backend/Cargo.toml +++ b/crates/uv-build-backend/Cargo.toml @@ -37,6 +37,7 @@ sha2 = { workspace = true } spdx = { workspace = true } tar = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } toml = { workspace = true } tracing = { workspace = true } version-ranges = { workspace = true } diff --git a/crates/uv-build-backend/src/lib.rs b/crates/uv-build-backend/src/lib.rs index cbec1f055..46aa61de5 100644 --- a/crates/uv-build-backend/src/lib.rs +++ b/crates/uv-build-backend/src/lib.rs @@ -24,7 +24,7 @@ use uv_pypi_types::{Identifier, IdentifierParseError}; use crate::metadata::ValidationError; -#[derive(Debug, Error)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-build-backend/src/metadata.rs b/crates/uv-build-backend/src/metadata.rs index 890110607..22aec1225 100644 --- a/crates/uv-build-backend/src/metadata.rs +++ b/crates/uv-build-backend/src/metadata.rs @@ -26,7 +26,7 @@ use crate::{BuildBackendSettings, Error}; /// By default, we ignore generated python files. pub(crate) const DEFAULT_EXCLUDES: &[&str] = &["__pycache__", "*.pyc", "*.pyo"]; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum ValidationError { /// The spec isn't clear about what the values in that field would be, and we only support the /// default value (UTF-8). diff --git a/crates/uv-build-frontend/Cargo.toml b/crates/uv-build-frontend/Cargo.toml index 83f8008d9..56c18265b 100644 --- a/crates/uv-build-frontend/Cargo.toml +++ b/crates/uv-build-frontend/Cargo.toml @@ -41,6 +41,7 @@ serde = { workspace = true } serde_json = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } toml_edit = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-build-frontend/src/error.rs b/crates/uv-build-frontend/src/error.rs index 7cfcfe373..73e2ee3c4 100644 --- a/crates/uv-build-frontend/src/error.rs +++ b/crates/uv-build-frontend/src/error.rs @@ -54,7 +54,7 @@ static TORCH_NOT_FOUND_RE: LazyLock = static DISTUTILS_NOT_FOUND_RE: LazyLock = LazyLock::new(|| Regex::new(r"ModuleNotFoundError: No module named 'distutils'").unwrap()); -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), @@ -120,7 +120,7 @@ enum MissingLibrary { DeprecatedModule(String, Version), } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct MissingHeaderCause { missing_library: MissingLibrary, package_name: Option, @@ -248,7 +248,7 @@ impl Display for MissingHeaderCause { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct BuildBackendError { message: String, exit_code: ExitStatus, @@ -287,7 +287,7 @@ impl Display for BuildBackendError { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct MissingHeaderError { message: String, exit_code: ExitStatus, diff --git a/crates/uv-cache-info/Cargo.toml b/crates/uv-cache-info/Cargo.toml index 6b10bbebe..e8cc0f6a2 100644 --- a/crates/uv-cache-info/Cargo.toml +++ b/crates/uv-cache-info/Cargo.toml @@ -21,6 +21,7 @@ globwalk = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } +traversable-error = { workspace = true } toml = { workspace = true } tracing = { workspace = true } walkdir = { workspace = true } diff --git a/crates/uv-cache-info/src/cache_info.rs b/crates/uv-cache-info/src/cache_info.rs index ce98cc513..e701f8a8e 100644 --- a/crates/uv-cache-info/src/cache_info.rs +++ b/crates/uv-cache-info/src/cache_info.rs @@ -9,7 +9,7 @@ use tracing::{debug, warn}; use crate::git_info::{Commit, Tags}; use crate::timestamp::Timestamp; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum CacheInfoError { #[error("Failed to parse glob patterns for `cache-keys`: {0}")] Glob(#[from] globwalk::GlobError), diff --git a/crates/uv-cache-info/src/git_info.rs b/crates/uv-cache-info/src/git_info.rs index ad566d13b..b53a56fb3 100644 --- a/crates/uv-cache-info/src/git_info.rs +++ b/crates/uv-cache-info/src/git_info.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use tracing::warn; use walkdir::WalkDir; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub(crate) enum GitInfoError { #[error("The repository at {0} is missing a `.git` directory")] MissingGitDir(PathBuf), diff --git a/crates/uv-client/Cargo.toml b/crates/uv-client/Cargo.toml index 64088676e..6b36a8007 100644 --- a/crates/uv-client/Cargo.toml +++ b/crates/uv-client/Cargo.toml @@ -51,6 +51,7 @@ serde = { workspace = true } serde_json = { workspace = true } sys-info = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tl = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true } diff --git a/crates/uv-client/src/error.rs b/crates/uv-client/src/error.rs index 74111bf5a..d9a8066df 100644 --- a/crates/uv-client/src/error.rs +++ b/crates/uv-client/src/error.rs @@ -11,7 +11,7 @@ use uv_normalize::PackageName; use crate::middleware::OfflineError; use crate::{html, FlatIndexError}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] #[error(transparent)] pub struct Error { kind: Box, @@ -147,7 +147,7 @@ impl From for Error { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum ErrorKind { #[error(transparent)] InvalidUrl(#[from] uv_distribution_types::ToUrlError), diff --git a/crates/uv-client/src/flat_index.rs b/crates/uv-client/src/flat_index.rs index 1a4a4f8f9..ce4809411 100644 --- a/crates/uv-client/src/flat_index.rs +++ b/crates/uv-client/src/flat_index.rs @@ -16,7 +16,7 @@ use crate::cached_client::{CacheControl, CachedClientError}; use crate::html::SimpleHtml; use crate::{CachedClient, Connectivity, Error, ErrorKind, OwnedArchive}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum FlatIndexError { #[error("Expected a file URL, but received: {0}")] NonFileUrl(Url), @@ -28,7 +28,7 @@ pub enum FlatIndexError { FindLinksUrl(Url, #[source] Error), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum FindLinksDirectoryError { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-client/src/html.rs b/crates/uv-client/src/html.rs index 106bd35a3..6b72d1b74 100644 --- a/crates/uv-client/src/html.rs +++ b/crates/uv-client/src/html.rs @@ -224,7 +224,7 @@ impl SimpleHtml { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum Error { #[error(transparent)] Utf8(#[from] std::str::Utf8Error), diff --git a/crates/uv-client/src/tls.rs b/crates/uv-client/src/tls.rs index 8a807234b..36d84599f 100644 --- a/crates/uv-client/src/tls.rs +++ b/crates/uv-client/src/tls.rs @@ -2,7 +2,7 @@ use reqwest::Identity; use std::ffi::OsStr; use std::io::Read; -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub(crate) enum CertificateError { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-configuration/Cargo.toml b/crates/uv-configuration/Cargo.toml index 1c195ab3d..434f6ccf2 100644 --- a/crates/uv-configuration/Cargo.toml +++ b/crates/uv-configuration/Cargo.toml @@ -39,6 +39,7 @@ serde = { workspace = true } serde-untagged = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-configuration/src/trusted_host.rs b/crates/uv-configuration/src/trusted_host.rs index 64fb14169..30875dd6b 100644 --- a/crates/uv-configuration/src/trusted_host.rs +++ b/crates/uv-configuration/src/trusted_host.rs @@ -73,7 +73,7 @@ impl serde::Serialize for TrustedHost { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum TrustedHostError { #[error("missing host for `--trusted-host`: `{0}`")] MissingHost(String), diff --git a/crates/uv-configuration/src/vcs.rs b/crates/uv-configuration/src/vcs.rs index 13689de11..8637d6a4c 100644 --- a/crates/uv-configuration/src/vcs.rs +++ b/crates/uv-configuration/src/vcs.rs @@ -5,7 +5,7 @@ use std::process::{Command, Stdio}; use serde::Deserialize; use uv_git::GIT; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum VersionControlError { #[error("Attempted to initialize a Git repository, but `git` was not found in PATH")] GitNotInstalled, diff --git a/crates/uv-dispatch/Cargo.toml b/crates/uv-dispatch/Cargo.toml index 06cb2db15..fd6c2b670 100644 --- a/crates/uv-dispatch/Cargo.toml +++ b/crates/uv-dispatch/Cargo.toml @@ -41,5 +41,6 @@ futures = { workspace = true } itertools = { workspace = true } rustc-hash = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 87b952529..77f0b709d 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -40,7 +40,7 @@ use uv_types::{ }; use uv_workspace::WorkspaceCache; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum BuildDispatchError { #[error(transparent)] BuildFrontend(#[from] AnyErrorBuild), diff --git a/crates/uv-distribution-filename/Cargo.toml b/crates/uv-distribution-filename/Cargo.toml index f30e79b3b..7bf16c917 100644 --- a/crates/uv-distribution-filename/Cargo.toml +++ b/crates/uv-distribution-filename/Cargo.toml @@ -27,6 +27,7 @@ rkyv = { workspace = true, features = ["smallvec-1"] } serde = { workspace = true } smallvec = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } url = { workspace = true } [dev-dependencies] diff --git a/crates/uv-distribution-filename/src/build_tag.rs b/crates/uv-distribution-filename/src/build_tag.rs index 64f49ef23..0cefc03cf 100644 --- a/crates/uv-distribution-filename/src/build_tag.rs +++ b/crates/uv-distribution-filename/src/build_tag.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use uv_small_str::SmallString; -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum BuildTagError { #[error("must not be empty")] Empty, diff --git a/crates/uv-distribution-filename/src/egg.rs b/crates/uv-distribution-filename/src/egg.rs index 9fe8f0bea..abc2c8a09 100644 --- a/crates/uv-distribution-filename/src/egg.rs +++ b/crates/uv-distribution-filename/src/egg.rs @@ -5,7 +5,7 @@ use thiserror::Error; use uv_normalize::{InvalidNameError, PackageName}; use uv_pep440::{Version, VersionParseError}; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum EggInfoFilenameError { #[error("The filename \"{0}\" does not end in `.egg-info`")] InvalidExtension(String), diff --git a/crates/uv-distribution-filename/src/extension.rs b/crates/uv-distribution-filename/src/extension.rs index a1a03d5ee..538b297d3 100644 --- a/crates/uv-distribution-filename/src/extension.rs +++ b/crates/uv-distribution-filename/src/extension.rs @@ -104,7 +104,7 @@ impl Display for SourceDistExtension { } } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum ExtensionError { #[error("`.whl`, `.tar.gz`, `.zip`, `.tar.bz2`, `.tar.lz`, `.tar.lzma`, `.tar.xz`, `.tar.zst`, `.tar`, `.tbz`, `.tgz`, `.tlz`, or `.txz`")] Dist, diff --git a/crates/uv-distribution-filename/src/source_dist.rs b/crates/uv-distribution-filename/src/source_dist.rs index 92f8fe3ae..c8a746f1b 100644 --- a/crates/uv-distribution-filename/src/source_dist.rs +++ b/crates/uv-distribution-filename/src/source_dist.rs @@ -141,7 +141,7 @@ impl Display for SourceDistFilename { } } -#[derive(Error, traversable_error::TraversableError, Debug, Clone)] +#[derive(traversable_error::TraversableError, Error, Debug, Clone)] pub struct SourceDistFilenameError { filename: String, kind: SourceDistFilenameErrorKind, @@ -157,7 +157,7 @@ impl Display for SourceDistFilenameError { } } -#[derive(Error, traversable_error::TraversableError, Debug, Clone)] +#[derive(traversable_error::TraversableError, Error, Debug, Clone)] enum SourceDistFilenameErrorKind { #[error("Name doesn't start with package name {0}")] Filename(PackageName), diff --git a/crates/uv-distribution-filename/src/wheel.rs b/crates/uv-distribution-filename/src/wheel.rs index 9a696e6d7..8b11ba677 100644 --- a/crates/uv-distribution-filename/src/wheel.rs +++ b/crates/uv-distribution-filename/src/wheel.rs @@ -355,7 +355,7 @@ impl Serialize for WheelFilename { } } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum WheelFilenameError { #[error("The wheel filename \"{0}\" is invalid: {1}")] InvalidWheelFileName(String, String), diff --git a/crates/uv-distribution-types/Cargo.toml b/crates/uv-distribution-types/Cargo.toml index 91943937e..e54627dff 100644 --- a/crates/uv-distribution-types/Cargo.toml +++ b/crates/uv-distribution-types/Cargo.toml @@ -44,6 +44,7 @@ schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } url = { workspace = true } version-ranges = { workspace = true } diff --git a/crates/uv-distribution-types/src/error.rs b/crates/uv-distribution-types/src/error.rs index fc1c4f588..dd0577c50 100644 --- a/crates/uv-distribution-types/src/error.rs +++ b/crates/uv-distribution-types/src/error.rs @@ -2,7 +2,7 @@ use url::Url; use uv_normalize::PackageName; -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-distribution-types/src/file.rs b/crates/uv-distribution-types/src/file.rs index 02b981768..0cc013905 100644 --- a/crates/uv-distribution-types/src/file.rs +++ b/crates/uv-distribution-types/src/file.rs @@ -11,7 +11,7 @@ use uv_pypi_types::{CoreMetadata, HashDigests, Yanked}; use uv_small_str::SmallString; /// Error converting [`uv_pypi_types::File`] to [`distribution_type::File`]. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum FileConversionError { #[error("Failed to parse `requires-python`: `{0}`")] RequiresPython(String, #[source] VersionSpecifiersParseError), @@ -197,7 +197,7 @@ impl Display for UrlString { } /// An error that occurs when a [`FileLocation`] is not a valid URL. -#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] +#[derive(Clone, Debug, Eq, PartialEq, traversable_error::TraversableError, thiserror::Error)] pub enum ToUrlError { /// An error that occurs when the base URL in [`FileLocation::Relative`] /// could not be parsed as a valid URL. diff --git a/crates/uv-distribution-types/src/index.rs b/crates/uv-distribution-types/src/index.rs index f5b9455c6..6f4955bb6 100644 --- a/crates/uv-distribution-types/src/index.rs +++ b/crates/uv-distribution-types/src/index.rs @@ -375,7 +375,7 @@ impl<'a> From<&'a IndexUrl> for IndexMetadataRef<'a> { } /// An error that can occur when parsing an [`Index`]. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum IndexSourceError { #[error(transparent)] Url(#[from] IndexUrlError), diff --git a/crates/uv-distribution-types/src/index_name.rs b/crates/uv-distribution-types/src/index_name.rs index 45d5d6039..9917c8073 100644 --- a/crates/uv-distribution-types/src/index_name.rs +++ b/crates/uv-distribution-types/src/index_name.rs @@ -87,7 +87,7 @@ impl Deref for IndexName { } /// An error that can occur when parsing an [`IndexName`]. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum IndexNameError { #[error("Index included a name, but the name was empty")] EmptyName, diff --git a/crates/uv-distribution-types/src/index_url.rs b/crates/uv-distribution-types/src/index_url.rs index 986402f21..2117f0b4f 100644 --- a/crates/uv-distribution-types/src/index_url.rs +++ b/crates/uv-distribution-types/src/index_url.rs @@ -160,7 +160,7 @@ impl Verbatim for IndexUrl { } /// An error that can occur when parsing an [`IndexUrl`]. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum IndexUrlError { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-distribution-types/src/installed.rs b/crates/uv-distribution-types/src/installed.rs index a9b78962e..11d67cd6c 100644 --- a/crates/uv-distribution-types/src/installed.rs +++ b/crates/uv-distribution-types/src/installed.rs @@ -17,7 +17,7 @@ use uv_pypi_types::{DirectUrl, MetadataError}; use crate::{DistributionMetadata, InstalledMetadata, InstalledVersion, Name, VersionOrUrlRef}; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum InstalledDistError { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-distribution-types/src/requirement.rs b/crates/uv-distribution-types/src/requirement.rs index c4ffde8b7..34439df69 100644 --- a/crates/uv-distribution-types/src/requirement.rs +++ b/crates/uv-distribution-types/src/requirement.rs @@ -22,7 +22,7 @@ use uv_pypi_types::{ ParsedUrl, ParsedUrlError, VerbatimParsedUrl, }; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum RequirementError { #[error(transparent)] VerbatimUrlError(#[from] uv_pep508::VerbatimUrlError), diff --git a/crates/uv-distribution/Cargo.toml b/crates/uv-distribution/Cargo.toml index 55975fcc4..215b976e0 100644 --- a/crates/uv-distribution/Cargo.toml +++ b/crates/uv-distribution/Cargo.toml @@ -49,6 +49,7 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } tempfile = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["compat"] } toml = { workspace = true } diff --git a/crates/uv-distribution/src/error.rs b/crates/uv-distribution/src/error.rs index ac6518145..2c870ec76 100644 --- a/crates/uv-distribution/src/error.rs +++ b/crates/uv-distribution/src/error.rs @@ -15,7 +15,7 @@ use uv_pep440::{Version, VersionSpecifiers}; use uv_pypi_types::{HashAlgorithm, HashDigest}; use uv_types::AnyErrorBuild; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum Error { #[error("Building source distributions is disabled")] NoBuild, @@ -162,7 +162,9 @@ impl From for Error { impl From for Error { fn from(error: reqwest_middleware::Error) -> Self { match error { - reqwest_middleware::Error::Middleware(error) => Self::ReqwestMiddlewareError(error), + reqwest_middleware::Error::Middleware(error) => { + Self::ReqwestMiddlewareError(error.into()) + } reqwest_middleware::Error::Reqwest(error) => { Self::Reqwest(WrappedReqwestError::from(error)) } diff --git a/crates/uv-distribution/src/metadata/lowering.rs b/crates/uv-distribution/src/metadata/lowering.rs index 440537926..61b8214bf 100644 --- a/crates/uv-distribution/src/metadata/lowering.rs +++ b/crates/uv-distribution/src/metadata/lowering.rs @@ -502,7 +502,7 @@ impl LoweredRequirement { /// An error parsing and merging `tool.uv.sources` with /// `project.{dependencies,optional-dependencies}`. -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum LoweringError { #[error("`{0}` is included as a workspace member, but is missing an entry in `tool.uv.sources` (e.g., `{0} = {{ workspace = true }}`)")] MissingWorkspaceSource(PackageName), diff --git a/crates/uv-distribution/src/metadata/mod.rs b/crates/uv-distribution/src/metadata/mod.rs index e63404455..f64ee278d 100644 --- a/crates/uv-distribution/src/metadata/mod.rs +++ b/crates/uv-distribution/src/metadata/mod.rs @@ -20,7 +20,7 @@ mod build_requires; mod lowering; mod requires_dist; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum MetadataError { #[error(transparent)] Workspace(#[from] WorkspaceError), diff --git a/crates/uv-extract/Cargo.toml b/crates/uv-extract/Cargo.toml index c25c2677d..7ccfb30ff 100644 --- a/crates/uv-extract/Cargo.toml +++ b/crates/uv-extract/Cargo.toml @@ -32,6 +32,7 @@ reqwest = { workspace = true } rustc-hash = { workspace = true } sha2 = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["compat"] } tracing = { workspace = true } diff --git a/crates/uv-extract/src/error.rs b/crates/uv-extract/src/error.rs index 09191bb0a..7a5e1ab1a 100644 --- a/crates/uv-extract/src/error.rs +++ b/crates/uv-extract/src/error.rs @@ -1,6 +1,6 @@ use std::{ffi::OsString, path::PathBuf}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum Error { #[error(transparent)] Zip(#[from] zip::result::ZipError), diff --git a/crates/uv-git-types/Cargo.toml b/crates/uv-git-types/Cargo.toml index 98ef751c9..41ba53400 100644 --- a/crates/uv-git-types/Cargo.toml +++ b/crates/uv-git-types/Cargo.toml @@ -18,5 +18,6 @@ workspace = true [dependencies] serde = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-git-types/src/lib.rs b/crates/uv-git-types/src/lib.rs index 2addc778a..96e06182a 100644 --- a/crates/uv-git-types/src/lib.rs +++ b/crates/uv-git-types/src/lib.rs @@ -9,7 +9,7 @@ mod github; mod oid; mod reference; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum GitUrlParseError { #[error( "Unsupported Git URL scheme `{0}:` in `{1}` (expected one of `https:`, `ssh:`, or `file:`)" diff --git a/crates/uv-git-types/src/oid.rs b/crates/uv-git-types/src/oid.rs index 912189e52..a13f0b019 100644 --- a/crates/uv-git-types/src/oid.rs +++ b/crates/uv-git-types/src/oid.rs @@ -24,7 +24,7 @@ impl GitOid { } } -#[derive(Debug, Error, traversable_error::TraversableError, PartialEq)] +#[derive(Debug, traversable_error::TraversableError, Error, PartialEq)] pub enum OidParseError { #[error("Object ID can be at most 40 hex characters")] TooLong, diff --git a/crates/uv-git/Cargo.toml b/crates/uv-git/Cargo.toml index 600a19367..0da7a629e 100644 --- a/crates/uv-git/Cargo.toml +++ b/crates/uv-git/Cargo.toml @@ -24,12 +24,14 @@ uv-static = { workspace = true } uv-version = { workspace = true } anyhow = { workspace = true } +anyhow_original = { package = "anyhow", version = "1" } cargo-util = { workspace = true } dashmap = { workspace = true } fs-err = { workspace = true, features = ["tokio"] } reqwest = { workspace = true, features = ["blocking"] } reqwest-middleware = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-git/src/git.rs b/crates/uv-git/src/git.rs index d77f507ce..864369d34 100644 --- a/crates/uv-git/src/git.rs +++ b/crates/uv-git/src/git.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use std::str::{self}; use std::sync::LazyLock; -use anyhow::{Context, Result}; +use anyhow_original::{Context, Result}; use cargo_util::{paths, ProcessBuilder}; use reqwest::StatusCode; use reqwest_middleware::ClientWithMiddleware; @@ -23,7 +23,7 @@ use uv_version::version; /// checkout is ready to go. See [`GitCheckout::reset`] for why we need this. const CHECKOUT_READY_LOCK: &str = ".ok"; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum GitError { #[error("Git executable not found. Ensure that Git is installed and available.")] GitNotFound, @@ -97,7 +97,7 @@ impl ReferenceOrOid<'_> { Self::Oid(s) => repo.rev_parse(&format!("{s}^0")), }; - result.with_context(|| anyhow::format_err!("failed to find {refkind} `{self}`")) + result.with_context(|| anyhow_original::format_err!("failed to find {refkind} `{self}`")) } /// Returns the kind of this [`ReferenceOrOid`]. diff --git a/crates/uv-git/src/resolver.rs b/crates/uv-git/src/resolver.rs index 9d9b216b7..736e7152c 100644 --- a/crates/uv-git/src/resolver.rs +++ b/crates/uv-git/src/resolver.rs @@ -16,14 +16,14 @@ use uv_version::version; use crate::{Fetch, GitSource, Reporter}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum GitResolverError { #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] Join(#[from] tokio::task::JoinError), #[error("Git operation failed")] - Git(#[source] anyhow::Error), + Git(#[source] anyhow_original::Error), #[error(transparent)] Reqwest(#[from] reqwest::Error), #[error(transparent)] @@ -158,7 +158,7 @@ impl GitResolver { let fetch = tokio::task::spawn_blocking(move || source.fetch()) .await? - .map_err(GitResolverError::Git)?; + .map_err(|err: anyhow::Error| GitResolverError::Git(err.into()))?; // Insert the resolved URL into the in-memory cache. This ensures that subsequent fetches // resolve to the same precise commit. diff --git a/crates/uv-globfilter/Cargo.toml b/crates/uv-globfilter/Cargo.toml index ca45a92f6..cdd2a3689 100644 --- a/crates/uv-globfilter/Cargo.toml +++ b/crates/uv-globfilter/Cargo.toml @@ -16,6 +16,7 @@ owo-colors = { workspace = true } regex = { workspace = true } regex-automata = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } walkdir = { workspace = true } diff --git a/crates/uv-globfilter/src/portable_glob.rs b/crates/uv-globfilter/src/portable_glob.rs index 00494a4bb..39351e8a4 100644 --- a/crates/uv-globfilter/src/portable_glob.rs +++ b/crates/uv-globfilter/src/portable_glob.rs @@ -5,7 +5,7 @@ use globset::{Glob, GlobBuilder}; use owo_colors::OwoColorize; use thiserror::Error; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum PortableGlobError { /// Shows the failing glob in the error message. #[error(transparent)] diff --git a/crates/uv-install-wheel/Cargo.toml b/crates/uv-install-wheel/Cargo.toml index dd9162e68..ce1e1519d 100644 --- a/crates/uv-install-wheel/Cargo.toml +++ b/crates/uv-install-wheel/Cargo.toml @@ -47,6 +47,7 @@ serde_json = { workspace = true } sha2 = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } walkdir = { workspace = true } diff --git a/crates/uv-install-wheel/src/lib.rs b/crates/uv-install-wheel/src/lib.rs index 1eb2787d8..264b2af80 100644 --- a/crates/uv-install-wheel/src/lib.rs +++ b/crates/uv-install-wheel/src/lib.rs @@ -36,7 +36,7 @@ pub struct Layout { } /// Note: The caller is responsible for adding the path of the wheel we're installing. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-installer/Cargo.toml b/crates/uv-installer/Cargo.toml index 02ab10b43..eeaa9d57f 100644 --- a/crates/uv-installer/Cargo.toml +++ b/crates/uv-installer/Cargo.toml @@ -44,6 +44,7 @@ rustc-hash = { workspace = true } same-file = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-installer/src/compile.rs b/crates/uv-installer/src/compile.rs index a76f681af..602840e75 100644 --- a/crates/uv-installer/src/compile.rs +++ b/crates/uv-installer/src/compile.rs @@ -22,7 +22,7 @@ const COMPILEALL_SCRIPT: &str = include_str!("pip_compileall.py"); /// This is longer than any compilation should ever take. const COMPILE_TIMEOUT: Duration = Duration::from_secs(60); -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum CompileError { #[error("Failed to list files in `site-packages`")] Walkdir(#[from] walkdir::Error), diff --git a/crates/uv-installer/src/preparer.rs b/crates/uv-installer/src/preparer.rs index b4ba673d1..78071cf74 100644 --- a/crates/uv-installer/src/preparer.rs +++ b/crates/uv-installer/src/preparer.rs @@ -210,7 +210,7 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> { } } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error("Building source distributions is disabled, but attempted to build `{0}`")] NoBuild(PackageName), diff --git a/crates/uv-installer/src/uninstall.rs b/crates/uv-installer/src/uninstall.rs index 42100e84f..9e432fce4 100644 --- a/crates/uv-installer/src/uninstall.rs +++ b/crates/uv-installer/src/uninstall.rs @@ -24,7 +24,7 @@ pub async fn uninstall( Ok(uninstall) } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum UninstallError { #[error("Unable to uninstall `{0}`. distutils-installed distributions do not include the metadata required to uninstall safely.")] Distutils(InstalledEggInfoFile), diff --git a/crates/uv-metadata/Cargo.toml b/crates/uv-metadata/Cargo.toml index b80547dca..47a499573 100644 --- a/crates/uv-metadata/Cargo.toml +++ b/crates/uv-metadata/Cargo.toml @@ -21,6 +21,7 @@ async_zip = { workspace = true } fs-err = { workspace = true } futures = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-metadata/src/lib.rs b/crates/uv-metadata/src/lib.rs index b54b82986..4bb370af8 100644 --- a/crates/uv-metadata/src/lib.rs +++ b/crates/uv-metadata/src/lib.rs @@ -15,7 +15,7 @@ use uv_pypi_types::ResolutionMetadata; use zip::ZipArchive; /// The caller is responsible for attaching the path or url we failed to read. -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error("Failed to read `dist-info` metadata from built wheel")] DistInfo, diff --git a/crates/uv-pep508/Cargo.toml b/crates/uv-pep508/Cargo.toml index 1fff96287..0aa18c754 100644 --- a/crates/uv-pep508/Cargo.toml +++ b/crates/uv-pep508/Cargo.toml @@ -33,6 +33,7 @@ schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive", "rc"] } smallvec = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true, optional = true } unicode-width = { workspace = true } url = { workspace = true, features = ["serde"] } diff --git a/crates/uv-pep508/src/lib.rs b/crates/uv-pep508/src/lib.rs index 1f91b36d5..308becb40 100644 --- a/crates/uv-pep508/src/lib.rs +++ b/crates/uv-pep508/src/lib.rs @@ -67,7 +67,7 @@ pub struct Pep508Error { } /// Either we have an error string from our parser or an upstream error from `url` -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Pep508ErrorSource { /// An error from our parser. #[error("{0}")] diff --git a/crates/uv-pep508/src/verbatim_url.rs b/crates/uv-pep508/src/verbatim_url.rs index 115223eb3..1e58369e8 100644 --- a/crates/uv-pep508/src/verbatim_url.rs +++ b/crates/uv-pep508/src/verbatim_url.rs @@ -317,7 +317,7 @@ impl Pep508Url for VerbatimUrl { } /// An error that can occur when parsing a [`VerbatimUrl`]. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum VerbatimUrlError { /// Failed to parse a URL. #[error(transparent)] diff --git a/crates/uv-platform-tags/Cargo.toml b/crates/uv-platform-tags/Cargo.toml index dcab932ad..1ea5a9b9a 100644 --- a/crates/uv-platform-tags/Cargo.toml +++ b/crates/uv-platform-tags/Cargo.toml @@ -23,6 +23,7 @@ rkyv = { workspace = true} rustc-hash = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } [dev-dependencies] insta = { version = "1.40.0" } diff --git a/crates/uv-platform-tags/src/abi_tag.rs b/crates/uv-platform-tags/src/abi_tag.rs index 70b11d8c2..83743f362 100644 --- a/crates/uv-platform-tags/src/abi_tag.rs +++ b/crates/uv-platform-tags/src/abi_tag.rs @@ -280,7 +280,7 @@ impl FromStr for AbiTag { } } -#[derive(Debug, thiserror::Error, PartialEq, Eq)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error, PartialEq, Eq)] pub enum ParseAbiTagError { #[error("Unknown ABI tag format: {0}")] UnknownFormat(String), diff --git a/crates/uv-platform-tags/src/language_tag.rs b/crates/uv-platform-tags/src/language_tag.rs index 8641664de..1246dc402 100644 --- a/crates/uv-platform-tags/src/language_tag.rs +++ b/crates/uv-platform-tags/src/language_tag.rs @@ -206,7 +206,7 @@ impl FromStr for LanguageTag { } } -#[derive(Debug, thiserror::Error, PartialEq, Eq)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error, PartialEq, Eq)] pub enum ParseLanguageTagError { #[error("Unknown language tag format: {0}")] UnknownFormat(String), diff --git a/crates/uv-platform-tags/src/platform.rs b/crates/uv-platform-tags/src/platform.rs index c0c81ec9b..15e8b17f7 100644 --- a/crates/uv-platform-tags/src/platform.rs +++ b/crates/uv-platform-tags/src/platform.rs @@ -5,7 +5,7 @@ use std::{fmt, io}; use thiserror::Error; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PlatformError { #[error(transparent)] IOError(#[from] io::Error), diff --git a/crates/uv-platform-tags/src/platform_tag.rs b/crates/uv-platform-tags/src/platform_tag.rs index 0ba46200d..6eaaf2e6d 100644 --- a/crates/uv-platform-tags/src/platform_tag.rs +++ b/crates/uv-platform-tags/src/platform_tag.rs @@ -620,7 +620,7 @@ impl FromStr for PlatformTag { } } -#[derive(Debug, thiserror::Error, PartialEq, Eq)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error, PartialEq, Eq)] pub enum ParsePlatformTagError { #[error("Unknown platform tag format: {0}")] UnknownFormat(String), diff --git a/crates/uv-platform-tags/src/tags.rs b/crates/uv-platform-tags/src/tags.rs index 1e20c348c..469da7bf6 100644 --- a/crates/uv-platform-tags/src/tags.rs +++ b/crates/uv-platform-tags/src/tags.rs @@ -10,7 +10,7 @@ use uv_small_str::SmallString; use crate::{AbiTag, Arch, LanguageTag, Os, Platform, PlatformError, PlatformTag}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum TagsError { #[error(transparent)] PlatformError(#[from] PlatformError), diff --git a/crates/uv-publish/Cargo.toml b/crates/uv-publish/Cargo.toml index 254d83d03..d7434472a 100644 --- a/crates/uv-publish/Cargo.toml +++ b/crates/uv-publish/Cargo.toml @@ -40,6 +40,7 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true , features = ["io"] } tracing = { workspace = true } diff --git a/crates/uv-publish/src/lib.rs b/crates/uv-publish/src/lib.rs index 4b2457f49..a443299cf 100644 --- a/crates/uv-publish/src/lib.rs +++ b/crates/uv-publish/src/lib.rs @@ -43,7 +43,7 @@ use uv_warnings::{warn_user, warn_user_once}; use crate::trusted_publishing::TrustedPublishingError; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PublishError { #[error("The publish path is not a valid glob pattern: `{0}`")] Pattern(String, #[source] PatternError), @@ -81,7 +81,7 @@ pub enum PublishError { } /// Failure to get the metadata for a specific file. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PublishPrepareError { #[error(transparent)] Io(#[from] io::Error), @@ -100,7 +100,7 @@ pub enum PublishPrepareError { } /// Failure in or after (HTTP) transport for a specific file. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PublishSendError { #[error("Failed to send POST request")] ReqwestMiddleware(#[source] reqwest_middleware::Error), diff --git a/crates/uv-publish/src/trusted_publishing.rs b/crates/uv-publish/src/trusted_publishing.rs index c9c1a6809..8ef57e693 100644 --- a/crates/uv-publish/src/trusted_publishing.rs +++ b/crates/uv-publish/src/trusted_publishing.rs @@ -14,7 +14,7 @@ use tracing::{debug, trace}; use url::Url; use uv_static::EnvVars; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum TrustedPublishingError { #[error("Environment variable {0} not set, is the `id-token: write` permission missing?")] MissingEnvVar(&'static str), diff --git a/crates/uv-pypi-types/Cargo.toml b/crates/uv-pypi-types/Cargo.toml index 2393240b9..3e971bbd6 100644 --- a/crates/uv-pypi-types/Cargo.toml +++ b/crates/uv-pypi-types/Cargo.toml @@ -36,6 +36,7 @@ schemars = { workspace = true, optional = true } serde = { workspace = true } serde-untagged = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } toml_edit = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-pypi-types/src/base_url.rs b/crates/uv-pypi-types/src/base_url.rs index 535407cf6..e35c1d400 100644 --- a/crates/uv-pypi-types/src/base_url.rs +++ b/crates/uv-pypi-types/src/base_url.rs @@ -20,7 +20,7 @@ pub fn base_url_join_relative(base: &str, relative: &str) -> Result for ConflictPackageRef<'_> { } /// An error that occurs when the given conflicting set is invalid somehow. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum ConflictError { /// An error for when there are zero conflicting items. #[error("Each set of conflicts must have at least two entries, but found none")] diff --git a/crates/uv-pypi-types/src/identifier.rs b/crates/uv-pypi-types/src/identifier.rs index ebb409286..54c134a04 100644 --- a/crates/uv-pypi-types/src/identifier.rs +++ b/crates/uv-pypi-types/src/identifier.rs @@ -12,7 +12,7 @@ use thiserror::Error; #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Identifier(Box); -#[derive(Debug, Clone, Error, traversable_error::TraversableError)] +#[derive(Debug, Clone, traversable_error::TraversableError, Error)] pub enum IdentifierParseError { #[error("An identifier must not be empty")] Empty, diff --git a/crates/uv-pypi-types/src/metadata/mod.rs b/crates/uv-pypi-types/src/metadata/mod.rs index 6297bf643..c315f7fa0 100644 --- a/crates/uv-pypi-types/src/metadata/mod.rs +++ b/crates/uv-pypi-types/src/metadata/mod.rs @@ -28,7 +28,7 @@ pub use requires_txt::RequiresTxt; /// /// /// The error type -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum MetadataError { #[error(transparent)] MailParse(#[from] MailParseError), diff --git a/crates/uv-pypi-types/src/parsed_url.rs b/crates/uv-pypi-types/src/parsed_url.rs index e7930b324..77498fa19 100644 --- a/crates/uv-pypi-types/src/parsed_url.rs +++ b/crates/uv-pypi-types/src/parsed_url.rs @@ -12,7 +12,7 @@ use uv_pep508::{ use crate::{ArchiveInfo, DirInfo, DirectUrl, VcsInfo, VcsKind}; -#[derive(Debug, Error)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum ParsedUrlError { #[error("Unsupported URL prefix `{prefix}` in URL: `{url}` ({message})")] UnsupportedUrlPrefix { diff --git a/crates/uv-pypi-types/src/simple_json.rs b/crates/uv-pypi-types/src/simple_json.rs index 45cc4e373..ff1ded802 100644 --- a/crates/uv-pypi-types/src/simple_json.rs +++ b/crates/uv-pypi-types/src/simple_json.rs @@ -570,7 +570,7 @@ impl IntoIterator for HashDigests { } } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum HashError { #[error("Unexpected hash (expected `:`): {0}")] InvalidStructure(String), diff --git a/crates/uv-python/Cargo.toml b/crates/uv-python/Cargo.toml index 25317017b..426eb3e95 100644 --- a/crates/uv-python/Cargo.toml +++ b/crates/uv-python/Cargo.toml @@ -56,6 +56,7 @@ sys-info = { workspace = true } target-lexicon = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["compat"] } tracing = { workspace = true } diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index eb9ee439c..dd0dfd01e 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -175,7 +175,7 @@ type FindPythonResult = Result; /// The result of failed Python installation discovery. /// /// See [`FindPythonResult`]. -#[derive(Clone, Debug, Error, traversable_error::TraversableError)] +#[derive(Clone, Debug, traversable_error::TraversableError, Error)] pub struct PythonNotFound { pub request: PythonRequest, pub python_preference: PythonPreference, @@ -209,7 +209,7 @@ pub enum PythonSource { ParentInterpreter, } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-python/src/downloads.rs b/crates/uv-python/src/downloads.rs index fb7a3c067..10c27f141 100644 --- a/crates/uv-python/src/downloads.rs +++ b/crates/uv-python/src/downloads.rs @@ -38,7 +38,7 @@ use crate::platform::{self, Arch, Libc, Os}; use crate::PythonVariant; use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest}; -#[derive(Error, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), @@ -1042,7 +1042,7 @@ impl Error { pub(crate) fn from_reqwest_middleware(url: Url, err: reqwest_middleware::Error) -> Self { match err { reqwest_middleware::Error::Middleware(error) => { - Self::NetworkMiddlewareError(url, error) + Self::NetworkMiddlewareError(url, error.into()) } reqwest_middleware::Error::Reqwest(error) => { Self::NetworkError(url, WrappedReqwestError::from(error)) diff --git a/crates/uv-python/src/environment.rs b/crates/uv-python/src/environment.rs index 27a10ca84..dd5d5b23a 100644 --- a/crates/uv-python/src/environment.rs +++ b/crates/uv-python/src/environment.rs @@ -33,13 +33,13 @@ struct PythonEnvironmentShared { /// The result of failed environment discovery. /// /// Generally this is cast from [`PythonNotFound`] by [`PythonEnvironment::find`]. -#[derive(Clone, Debug, Error, traversable_error::TraversableError)] +#[derive(Clone, Debug, traversable_error::TraversableError, Error)] pub struct EnvironmentNotFound { request: PythonRequest, preference: EnvironmentPreference, } -#[derive(Clone, Debug, Error, traversable_error::TraversableError)] +#[derive(Clone, Debug, traversable_error::TraversableError, Error)] pub struct InvalidEnvironment { path: PathBuf, pub kind: InvalidEnvironmentKind, diff --git a/crates/uv-python/src/implementation.rs b/crates/uv-python/src/implementation.rs index 7de5d9eff..0ff90eb29 100644 --- a/crates/uv-python/src/implementation.rs +++ b/crates/uv-python/src/implementation.rs @@ -4,7 +4,7 @@ use std::{ }; use thiserror::Error; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error("Unknown Python implementation `{0}`")] UnknownImplementation(String), diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index 0c581571a..3003f076e 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -253,7 +253,7 @@ impl PythonInstallation { } } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PythonInstallationKeyError { #[error("Failed to parse Python installation key `{0}`: {1}")] ParseError(String, String), diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 2e3e90f17..22f49c5a7 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -598,7 +598,7 @@ impl ExternallyManaged { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct UnexpectedResponseError { #[source] pub(super) err: serde_json::Error, @@ -636,7 +636,7 @@ impl Display for UnexpectedResponseError { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct StatusCodeError { pub(super) code: ExitStatus, pub(super) stdout: String, @@ -673,7 +673,7 @@ impl Display for StatusCodeError { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error("Failed to query Python interpreter")] Io(#[from] io::Error), @@ -701,7 +701,7 @@ pub enum Error { Encode(#[from] rmp_serde::encode::Error), } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub struct BrokenSymlink { pub path: PathBuf, /// Whether the interpreter path looks like a virtual environment. @@ -735,7 +735,7 @@ enum InterpreterInfoResult { Success(Box), } -#[derive(Debug, Error, traversable_error::TraversableError, Deserialize, Serialize)] +#[derive(Debug, traversable_error::TraversableError, Error, Deserialize, Serialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum InterpreterInfoError { #[error("Could not detect a glibc or a musl libc (while running on Linux)")] diff --git a/crates/uv-python/src/lib.rs b/crates/uv-python/src/lib.rs index ac66c488a..1c4b4d29d 100644 --- a/crates/uv-python/src/lib.rs +++ b/crates/uv-python/src/lib.rs @@ -63,7 +63,7 @@ pub(crate) fn current_dir() -> Result { .unwrap_or(std::env::current_dir()) } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-python/src/libc.rs b/crates/uv-python/src/libc.rs index 48dc39af0..6718a1bd5 100644 --- a/crates/uv-python/src/libc.rs +++ b/crates/uv-python/src/libc.rs @@ -14,7 +14,7 @@ use thiserror::Error; use tracing::trace; use uv_fs::Simplified; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum LibcDetectionError { #[error("Could not detect either glibc version nor musl libc version, at least one of which is required")] NoLibcFound, diff --git a/crates/uv-python/src/macos_dylib.rs b/crates/uv-python/src/macos_dylib.rs index a77284da0..11d025ca8 100644 --- a/crates/uv-python/src/macos_dylib.rs +++ b/crates/uv-python/src/macos_dylib.rs @@ -31,7 +31,7 @@ pub fn patch_dylib_install_name(dylib: PathBuf) -> Result<(), Error> { Ok(()) } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-python/src/managed.rs b/crates/uv-python/src/managed.rs index d50399d35..2b226a01c 100644 --- a/crates/uv-python/src/managed.rs +++ b/crates/uv-python/src/managed.rs @@ -27,7 +27,7 @@ use crate::platform::{Arch, Libc, Os}; use crate::python_version::PythonVersion; use crate::{macos_dylib, sysconfig, PythonRequest, PythonVariant}; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-python/src/platform.rs b/crates/uv-python/src/platform.rs index 23d2e7821..7ad66d226 100644 --- a/crates/uv-python/src/platform.rs +++ b/crates/uv-python/src/platform.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use std::{fmt, str::FromStr}; use thiserror::Error; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error("Unknown operating system: {0}")] UnknownOs(String), diff --git a/crates/uv-python/src/sysconfig/mod.rs b/crates/uv-python/src/sysconfig/mod.rs index 211b15e7d..e5089df62 100644 --- a/crates/uv-python/src/sysconfig/mod.rs +++ b/crates/uv-python/src/sysconfig/mod.rs @@ -381,7 +381,7 @@ fn patch_pkgconfig(contents: &str) -> Option { } } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-python/src/sysconfig/parser.rs b/crates/uv-python/src/sysconfig/parser.rs index f505cc7d0..7bc63cadb 100644 --- a/crates/uv-python/src/sysconfig/parser.rs +++ b/crates/uv-python/src/sysconfig/parser.rs @@ -249,7 +249,7 @@ const fn is_python_whitespace(c: char) -> bool { ) } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error("Missing opening brace")] MissingOpenBrace, diff --git a/crates/uv-python/src/virtualenv.rs b/crates/uv-python/src/virtualenv.rs index 4880f5c9b..210f5c957 100644 --- a/crates/uv-python/src/virtualenv.rs +++ b/crates/uv-python/src/virtualenv.rs @@ -48,7 +48,7 @@ pub struct PyVenvConfiguration { pub(crate) version: Option, } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-python/src/windows_registry.rs b/crates/uv-python/src/windows_registry.rs index 52393f98d..0492d8d82 100644 --- a/crates/uv-python/src/windows_registry.rs +++ b/crates/uv-python/src/windows_registry.rs @@ -125,7 +125,7 @@ fn read_registry_entry(company: &str, tag: &str, tag_key: &Key) -> Option, #[source] uv_distribution::Error), diff --git a/crates/uv-requirements/src/source_tree.rs b/crates/uv-requirements/src/source_tree.rs index 3560254a0..8896189d8 100644 --- a/crates/uv-requirements/src/source_tree.rs +++ b/crates/uv-requirements/src/source_tree.rs @@ -164,7 +164,7 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> { path.user_display() ) })?; - let source_tree = source_tree.parent().ok_or_else(|| { + let source_tree = source_tree.parent().ok_or_else(|| -> anyhow::Error { anyhow::anyhow!( "The file `{}` appears to be a `pyproject.toml`, `setup.py`, or `setup.cfg` file, which must be in a directory", path.user_display() diff --git a/crates/uv-resolver/Cargo.toml b/crates/uv-resolver/Cargo.toml index 79645906a..e81684dde 100644 --- a/crates/uv-resolver/Cargo.toml +++ b/crates/uv-resolver/Cargo.toml @@ -61,6 +61,7 @@ serde = { workspace = true } smallvec = { workspace = true } textwrap = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } toml = { workspace = true } diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 9d7ce0b4b..76a8eb35c 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -30,7 +30,7 @@ use crate::resolver::{ }; use crate::{InMemoryIndex, Options}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum ResolveError { #[error(transparent)] Client(#[from] uv_client::Error), diff --git a/crates/uv-resolver/src/lock/export/pylock_toml.rs b/crates/uv-resolver/src/lock/export/pylock_toml.rs index c6ec892af..9de3cc78d 100644 --- a/crates/uv-resolver/src/lock/export/pylock_toml.rs +++ b/crates/uv-resolver/src/lock/export/pylock_toml.rs @@ -40,7 +40,7 @@ use crate::lock::{each_element_on_its_line_array, Source, WheelTagHint}; use crate::resolution::ResolutionGraphNode; use crate::{Installable, LockError, RequiresPython, ResolverOutput}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum PylockTomlErrorKind { #[error("Package `{0}` includes both a registry (`packages.wheels`) and a directory source (`packages.directory`)")] WheelWithDirectory(PackageName), @@ -114,7 +114,7 @@ pub enum PylockTomlErrorKind { Deserialize(#[from] toml::de::Error), } -#[derive(Debug)] +#[derive(Debug, traversable_error::TraversableError)] pub struct PylockTomlError { kind: Box, hint: Option, diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index bf999b5dd..0641483dd 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -17,8 +17,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::Serializer; use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value}; use tracing::debug; +use traversable_error::TraversableError; use url::Url; - use uv_cache_key::RepositoryUrl; use uv_configuration::{BuildOptions, Constraints}; use uv_distribution::{DistributionDatabase, FlatRequiresDist}; @@ -4704,7 +4704,7 @@ fn normalize_requirement( } } -#[derive(Debug)] +#[derive(Debug, TraversableError)] pub struct LockError { kind: Box, hint: Option, @@ -5103,7 +5103,7 @@ impl std::fmt::Display for WheelTagHint { /// For example, if there are two or more duplicative distributions given /// to `Lock::new`, then an error is returned. It's likely that the fault /// is with the caller somewhere in such cases. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] enum LockErrorKind { /// An error that occurs when multiple packages with the same /// ID were found. @@ -5411,7 +5411,7 @@ enum LockErrorKind { } /// An error that occurs when a source string could not be parsed. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] enum SourceParseError { /// An error that occurs when the URL in the source is invalid. #[error("Invalid URL in source `{given}`")] diff --git a/crates/uv-resolver/src/preferences.rs b/crates/uv-resolver/src/preferences.rs index 116d94d87..12388fe64 100644 --- a/crates/uv-resolver/src/preferences.rs +++ b/crates/uv-resolver/src/preferences.rs @@ -15,7 +15,7 @@ use crate::lock::PylockTomlPackage; use crate::universal_marker::UniversalMarker; use crate::{LockError, ResolverEnvironment}; -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum PreferenceError { #[error(transparent)] Hash(#[from] HashError), diff --git a/crates/uv-scripts/Cargo.toml b/crates/uv-scripts/Cargo.toml index 2554e6b23..31b00b50f 100644 --- a/crates/uv-scripts/Cargo.toml +++ b/crates/uv-scripts/Cargo.toml @@ -22,5 +22,6 @@ indoc = { workspace = true } memchr = { workspace = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } +traversable-error = { workspace = true } toml = { workspace = true } url = { workspace = true } diff --git a/crates/uv-scripts/src/lib.rs b/crates/uv-scripts/src/lib.rs index a452cf1b3..d7684597e 100644 --- a/crates/uv-scripts/src/lib.rs +++ b/crates/uv-scripts/src/lib.rs @@ -368,7 +368,7 @@ pub struct ToolUv { pub sources: Option>, } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Pep723Error { #[error("An opening tag (`# /// script`) was found without a closing tag (`# ///`). Ensure that every line between the opening and closing tags (including empty lines) starts with a leading `#`.")] UnclosedBlock, diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index fd4c3c739..c8d8d6c3a 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -39,6 +39,7 @@ schemars = { workspace = true, optional = true } serde = { workspace = true } textwrap = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } toml = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index 54ae4e261..c191438be 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -225,7 +225,7 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> { Ok(()) } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-tool/Cargo.toml b/crates/uv-tool/Cargo.toml index d01a3209d..14473d704 100644 --- a/crates/uv-tool/Cargo.toml +++ b/crates/uv-tool/Cargo.toml @@ -35,6 +35,7 @@ fs-err = { workspace = true } pathdiff = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } toml = { workspace = true } toml_edit = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index cef8012b5..e51e8b425 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -27,7 +27,7 @@ use uv_static::EnvVars; mod receipt; mod tool; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-torch/Cargo.toml b/crates/uv-torch/Cargo.toml index d173c6ede..38657237c 100644 --- a/crates/uv-torch/Cargo.toml +++ b/crates/uv-torch/Cargo.toml @@ -22,6 +22,7 @@ fs-err = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } url = { workspace = true } diff --git a/crates/uv-torch/src/accelerator.rs b/crates/uv-torch/src/accelerator.rs index c7e4aa22c..6eb30d77f 100644 --- a/crates/uv-torch/src/accelerator.rs +++ b/crates/uv-torch/src/accelerator.rs @@ -5,7 +5,7 @@ use tracing::debug; use uv_pep440::Version; use uv_static::EnvVars; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub enum AcceleratorError { #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/uv-trampoline-builder/Cargo.toml b/crates/uv-trampoline-builder/Cargo.toml index d5d5436fe..d1b336bdf 100644 --- a/crates/uv-trampoline-builder/Cargo.toml +++ b/crates/uv-trampoline-builder/Cargo.toml @@ -26,6 +26,7 @@ uv-fs = { workspace = true } fs-err = {workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } zip = { workspace = true } [dev-dependencies] diff --git a/crates/uv-trampoline-builder/src/lib.rs b/crates/uv-trampoline-builder/src/lib.rs index 3f9920842..0aa018d84 100644 --- a/crates/uv-trampoline-builder/src/lib.rs +++ b/crates/uv-trampoline-builder/src/lib.rs @@ -171,7 +171,7 @@ impl LauncherKind { } /// Note: The caller is responsible for adding the path of the wheel we're installing. -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-types/Cargo.toml b/crates/uv-types/Cargo.toml index 08a00e006..b54ecc795 100644 --- a/crates/uv-types/Cargo.toml +++ b/crates/uv-types/Cargo.toml @@ -32,6 +32,7 @@ uv-workspace = { workspace = true } anyhow = { workspace = true } rustc-hash = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } url = { workspace = true } [features] diff --git a/crates/uv-types/src/hash.rs b/crates/uv-types/src/hash.rs index 27fb90392..733951aee 100644 --- a/crates/uv-types/src/hash.rs +++ b/crates/uv-types/src/hash.rs @@ -323,7 +323,7 @@ impl HashStrategy { } } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum HashStrategyError { #[error(transparent)] Hash(#[from] HashError), diff --git a/crates/uv-virtualenv/Cargo.toml b/crates/uv-virtualenv/Cargo.toml index e9610176b..505a84753 100644 --- a/crates/uv-virtualenv/Cargo.toml +++ b/crates/uv-virtualenv/Cargo.toml @@ -30,6 +30,7 @@ fs-err = { workspace = true } itertools = { workspace = true } pathdiff = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tracing = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] diff --git a/crates/uv-virtualenv/src/lib.rs b/crates/uv-virtualenv/src/lib.rs index 092e445b1..b8ee612c1 100644 --- a/crates/uv-virtualenv/src/lib.rs +++ b/crates/uv-virtualenv/src/lib.rs @@ -7,7 +7,7 @@ use uv_python::{Interpreter, PythonEnvironment}; mod virtualenv; -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), diff --git a/crates/uv-workspace/Cargo.toml b/crates/uv-workspace/Cargo.toml index 59bc02f29..e92f29ece 100644 --- a/crates/uv-workspace/Cargo.toml +++ b/crates/uv-workspace/Cargo.toml @@ -38,6 +38,7 @@ rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } toml = { workspace = true } toml_edit = { workspace = true } diff --git a/crates/uv-workspace/src/dependency_groups.rs b/crates/uv-workspace/src/dependency_groups.rs index 7536a5bbb..08391f05e 100644 --- a/crates/uv-workspace/src/dependency_groups.rs +++ b/crates/uv-workspace/src/dependency_groups.rs @@ -141,7 +141,7 @@ impl IntoIterator for FlatDependencyGroups { } } -#[derive(Debug, Error, traversable_error::TraversableError)] +#[derive(Debug, traversable_error::TraversableError, Error)] pub enum DependencyGroupError { #[error("Failed to parse entry in group `{0}`: `{1}`")] GroupParseError( diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index 177ff1689..bf39da429 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -30,7 +30,7 @@ use uv_pypi_types::{ Conflicts, DependencyGroups, SchemaConflicts, SupportedEnvironments, VerbatimParsedUrl, }; -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum PyprojectTomlError { #[error(transparent)] TomlSyntax(#[from] toml_edit::TomlError), @@ -1331,7 +1331,7 @@ impl<'de> Deserialize<'de> for Source { } } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum SourceError { #[error("Failed to resolve Git reference: `{0}`")] UnresolvedReference(String), diff --git a/crates/uv-workspace/src/pyproject_mut.rs b/crates/uv-workspace/src/pyproject_mut.rs index a0c935a9d..3b7b62377 100644 --- a/crates/uv-workspace/src/pyproject_mut.rs +++ b/crates/uv-workspace/src/pyproject_mut.rs @@ -28,7 +28,7 @@ pub struct PyProjectTomlMut { target: DependencyTarget, } -#[derive(Error, traversable_error::TraversableError, Debug)] +#[derive(traversable_error::TraversableError, Error, Debug)] pub enum Error { #[error("Failed to parse `pyproject.toml`")] Parse(#[from] Box), diff --git a/crates/uv-workspace/src/workspace.rs b/crates/uv-workspace/src/workspace.rs index 374b9e762..811a7dc22 100644 --- a/crates/uv-workspace/src/workspace.rs +++ b/crates/uv-workspace/src/workspace.rs @@ -40,7 +40,7 @@ struct WorkspaceCacheKey { #[derive(Debug, Default, Clone)] pub struct WorkspaceCache(Arc>>); -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub enum WorkspaceError { // Workspace structure errors. #[error("No `pyproject.toml` found in current directory or any parent directory")] diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index a29a17feb..2fbda015f 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -89,6 +89,7 @@ serde_json = { workspace = true } tempfile = { workspace = true } textwrap = { workspace = true } thiserror = { workspace = true } +traversable-error = { workspace = true } tokio = { workspace = true } toml = { workspace = true } toml_edit = { workspace = true } diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index fca41066d..3fec5e8c0 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -45,7 +45,7 @@ use crate::commands::ExitStatus; use crate::printer::Printer; use crate::settings::{NetworkSettings, ResolverSettings}; -#[derive(Debug, Error)] +#[derive(Debug, traversable_error::TraversableError, Error)] enum Error { #[error(transparent)] Io(#[from] io::Error), @@ -369,7 +369,9 @@ async fn build_impl( } } Err(err) => { - #[derive(Debug, miette::Diagnostic, thiserror::Error)] + #[derive( + Debug, miette::Diagnostic, traversable_error::TraversableError, thiserror::Error, + )] #[error("Failed to build `{source}`", source = source.cyan())] #[diagnostic()] struct Diagnostic { diff --git a/crates/uv/src/commands/diagnostics.rs b/crates/uv/src/commands/diagnostics.rs index 5ddff0737..3c78b7c1a 100644 --- a/crates/uv/src/commands/diagnostics.rs +++ b/crates/uv/src/commands/diagnostics.rs @@ -140,7 +140,7 @@ pub(crate) fn dist_error( cause: Arc, help: Option, ) { - #[derive(Debug, miette::Diagnostic, thiserror::Error)] + #[derive(Debug, miette::Diagnostic, traversable_error::TraversableError, thiserror::Error)] #[error("{kind} `{dist}`")] #[diagnostic()] struct Diagnostic { @@ -188,7 +188,7 @@ pub(crate) fn requested_dist_error( cause: Arc, help: Option, ) { - #[derive(Debug, miette::Diagnostic, thiserror::Error)] + #[derive(Debug, miette::Diagnostic, traversable_error::TraversableError, thiserror::Error)] #[error("{kind} `{dist}`")] #[diagnostic()] struct Diagnostic { @@ -242,7 +242,7 @@ pub(crate) fn no_solution_context(err: &uv_resolver::NoSolutionError, context: & /// Render a [`uv_resolver::NoSolutionError`] with a help message. pub(crate) fn no_solution_hint(err: uv_resolver::NoSolutionError, help: String) { - #[derive(Debug, miette::Diagnostic, thiserror::Error)] + #[derive(Debug, miette::Diagnostic, traversable_error::TraversableError, thiserror::Error)] #[error("{header}")] #[diagnostic()] struct Error { diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index c6fafe409..6d5596385 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -686,7 +686,7 @@ pub(crate) async fn pip_compile( // Convert the resolution to a `pylock.toml` file. let export = PylockToml::from_resolution(&resolution, &no_emit_packages, install_path)?; - write!(writer, "{}", export.to_toml()?)?; + write!(writer, "{}", export.to_toml().map_err(anyhow::Error::new)?)?; } } diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index 46d66e678..fedb8112b 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -177,7 +177,7 @@ pub(crate) async fn pip_list( .map(|url| url.to_file_path().unwrap().simplified_display().to_string()), }) .collect_vec(); - let output = serde_json::to_string(&rows)?; + let output = serde_json::to_string(&rows).map_err(anyhow::Error::new)?; println!("{output}"); } ListFormat::Columns if results.is_empty() => {} diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index 09278cd63..87880ac98 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -837,7 +837,7 @@ pub(crate) fn diagnose_environment( Ok(()) } -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub(crate) enum Error { #[error("Failed to prepare distributions")] Prepare(#[from] uv_installer::PrepareError), diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index 2918cf442..63f5aeaf2 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -341,7 +341,7 @@ pub(crate) async fn export( )?; writeln!(writer, "{}", format!("# {}", cmd()).green())?; } - write!(writer, "{}", export.to_toml()?)?; + write!(writer, "{}", export.to_toml().map_err(anyhow::Error::new)?)?; } } diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index 89f43723a..11639f033 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -123,7 +123,7 @@ pub(crate) async fn init( // Pre-normalize the package name by removing any leading or trailing // whitespace, and replacing any internal whitespace with hyphens. let name = name.trim().replace(' ', "-"); - PackageName::from_owned(name)? + PackageName::from_owned(name).map_err(anyhow::Error::new)? } }; @@ -611,7 +611,10 @@ async fn init_project( &workspace.pyproject_toml().raw, DependencyTarget::PyProjectToml, )?; - pyproject.add_workspace(path.strip_prefix(workspace.install_path())?)?; + pyproject.add_workspace( + path.strip_prefix(workspace.install_path()) + .map_err(anyhow::Error::new)?, + )?; // Save the modified `pyproject.toml`. fs_err::write( diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 70eedb0f6..1b362deb9 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -68,7 +68,7 @@ pub(crate) mod run; pub(crate) mod sync; pub(crate) mod tree; -#[derive(thiserror::Error, Debug)] +#[derive(traversable_error::TraversableError, thiserror::Error, Debug)] pub(crate) enum ProjectError { #[error("The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`.")] LockMismatch(Box), @@ -777,7 +777,7 @@ impl std::fmt::Display for EnvironmentKind { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, traversable_error::TraversableError, thiserror::Error)] pub(crate) enum EnvironmentIncompatibilityError { #[error("The {0} environment's Python version does not satisfy the request: `{1}`")] PythonRequest(EnvironmentKind, PythonRequest), diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 8fe2081c8..508b0e34c 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -1110,7 +1110,8 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl .iter() .flat_map(std::env::split_paths), ), - )?; + ) + .context("Failed to build new PATH variable")?; process.env(EnvVars::PATH, new_path); // Increment recursion depth counter. @@ -1466,7 +1467,9 @@ impl RunCommand { // We don't do this check on Windows since the file path would // be invalid anyway, and thus couldn't refer to a local file. if !cfg!(unix) || matches!(target_path.try_exists(), Ok(false)) { - let url = Url::parse(&target.to_string_lossy())?; + let url = Url::parse(&target.to_string_lossy()).with_context(|| { + format!("Failed to parse as URL: {}", target.user_display()) + })?; let file_stem = url .path_segments() @@ -1483,14 +1486,22 @@ impl RunCommand { .native_tls(network_settings.native_tls) .allow_insecure_host(network_settings.allow_insecure_host.clone()) .build(); - let response = client.for_host(&url).get(url.clone()).send().await?; + let response = client + .for_host(&url) + .get(url.clone()) + .send() + .await + .map_err(anyhow::Error::new)?; // Stream the response to the file. let mut writer = file.as_file(); let mut reader = response.bytes_stream(); while let Some(chunk) = reader.next().await { use std::io::Write; - writer.write_all(&chunk?)?; + let chunk = chunk.with_context(|| format!("Failed to download: {url}"))?; + writer + .write_all(&chunk) + .with_context(|| format!("Failed to write to temporary file"))?; } return Ok(Self::PythonRemote(url, file, args.to_vec())); diff --git a/crates/uv/src/commands/python/list.rs b/crates/uv/src/commands/python/list.rs index 5236b768d..8a5dc8999 100644 --- a/crates/uv/src/commands/python/list.rs +++ b/crates/uv/src/commands/python/list.rs @@ -249,7 +249,11 @@ pub(crate) async fn list( }) }) .collect::>>()?; - writeln!(printer.stdout(), "{}", serde_json::to_string(&data)?)?; + writeln!( + printer.stdout(), + "{}", + serde_json::to_string(&data).map_err(anyhow::Error::new)? + )?; } PythonListFormat::Text => { // Compute the width of the first column. diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index e64ac8728..5e1943411 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -167,7 +167,7 @@ pub(crate) async fn run( if let Some(ref from) = from { if has_python_script_ext(Path::new(from)) { - let package_name = PackageName::from_str(from)?; + let package_name = PackageName::from_str(from).map_err(anyhow::Error::new)?; return Err(anyhow::anyhow!( "It looks you provided a Python script to `--from`, which is not supported\n\n{}{} If you meant to run a command from the `{}` package, use the normalized package name instead to disambiguate, e.g., `{}`", "hint".bold().cyan(), @@ -191,7 +191,7 @@ pub(crate) async fn run( format!("uv run {}", target_path.user_display().cyan()), )) } else { - let package_name = PackageName::from_str(target)?; + let package_name = PackageName::from_str(target).map_err(anyhow::Error::new)?; Err(anyhow::anyhow!( "It looks you provided a Python script to run, which is not supported supported by `{}`\n\n{}{} We did not find a script at the requested path. If you meant to run a command from the `{}` package, pass the normalized package name to `--from` to disambiguate, e.g., `{}`", invocation_source, diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index 05055533f..b4a2ee771 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -64,7 +64,9 @@ pub(crate) async fn upgrade( } else { let mut map = BTreeMap::new(); for name in names { - let requirement = Requirement::from(uv_pep508::Requirement::parse(&name, &*CWD)?); + let requirement = Requirement::from( + uv_pep508::Requirement::parse(&name, &*CWD).map_err(anyhow::Error::new)?, + ); map.entry(requirement.name.clone()) .or_insert_with(Vec::new) .push(requirement); diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index c1bd9e6d0..1a1884d17 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -103,7 +103,7 @@ pub(crate) async fn venv( } } -#[derive(Error, traversable_error::TraversableError, Debug, Diagnostic)] +#[derive(traversable_error::TraversableError, Error, Debug, Diagnostic)] enum VenvError { #[error("Failed to create virtualenv")] #[diagnostic(code(uv::venv::creation))] diff --git a/crates/uv/src/commands/version.rs b/crates/uv/src/commands/version.rs index 831ccadc7..d14071751 100644 --- a/crates/uv/src/commands/version.rs +++ b/crates/uv/src/commands/version.rs @@ -103,7 +103,7 @@ pub(crate) async fn project_version( )); } _ => { - return Err(err)?; + return Err(err).map_err(anyhow::Error::new)?; } }, } @@ -160,7 +160,8 @@ fn print_version( } VersionFormat::Json => { let final_version = new_version.unwrap_or(old_version); - let string = serde_json::to_string_pretty(&final_version)?; + let string = + serde_json::to_string_pretty(&final_version).map_err(anyhow::Error::new)?; writeln!(printer.stdout(), "{string}")?; } } diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 48b502290..96097d600 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -295,7 +295,8 @@ async fn run(mut cli: Cli) -> Result { // Enforce the required version. if let Some(required_version) = globals.required_version.as_ref() { - let package_version = uv_pep440::Version::from_str(uv_version::version())?; + let package_version = + uv_pep440::Version::from_str(uv_version::version()).map_err(anyhow::Error::new)?; if !required_version.contains(&package_version) { return Err(anyhow::anyhow!( "Required uv version `{required_version}` does not match the running version `{package_version}`", @@ -354,7 +355,8 @@ async fn run(mut cli: Cli) -> Result { ) .build(), ) - }))?; + })) + .map_err(anyhow::Error::new)?; // Don't initialize the rayon threadpool yet, this is too costly when we're doing a noop sync. uv_configuration::RAYON_PARALLELISM.store(globals.concurrency.installs, Ordering::SeqCst);