Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/uv-dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ impl<'a> BuildContext for BuildDispatch<'a> {
let graph = resolver.resolve().await.with_context(|| {
format!(
"No solution found when resolving: {}",
requirements.iter().map(ToString::to_string).join(", "),
requirements
.iter()
.map(|requirement| format!("`{requirement}`"))
.join(", ")
)
})?;
Ok(Resolution::from(graph))
Expand Down
2 changes: 0 additions & 2 deletions crates/uv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ uv-cache-info = { workspace = true }
uv-cli = { workspace = true }
uv-client = { workspace = true }
uv-configuration = { workspace = true }
uv-console = { workspace = true }
uv-dispatch = { workspace = true }
uv-distribution = { workspace = true }
uv-extract = { workspace = true }
Expand Down Expand Up @@ -58,7 +57,6 @@ axoupdater = { workspace = true, features = [
"tokio",
], optional = true }
clap = { workspace = true, features = ["derive", "string", "wrap_help"] }
console = { workspace = true }
ctrlc = { workspace = true }
flate2 = { workspace = true, default-features = false }
fs-err = { workspace = true, features = ["tokio"] }
Expand Down
112 changes: 112 additions & 0 deletions crates/uv/src/commands/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use distribution_types::{Name, SourceDist};
use owo_colors::OwoColorize;
use rustc_hash::FxHashMap;
use std::str::FromStr;
use std::sync::LazyLock;
use uv_normalize::PackageName;

/// Static map of common package name typos or misconfigurations to their correct package names.
static SUGGESTIONS: LazyLock<FxHashMap<PackageName, PackageName>> = LazyLock::new(|| {
let suggestions: Vec<(String, String)> =
serde_json::from_str(include_str!("suggestions.json")).unwrap();
suggestions
.iter()
.map(|(k, v)| {
(
PackageName::from_str(k).unwrap(),
PackageName::from_str(v).unwrap(),
)
})
.collect()
});

/// Render a [`uv_resolver::ResolveError::FetchAndBuild`] with a help message.
pub(crate) fn fetch_and_build(sdist: Box<SourceDist>, cause: uv_distribution::Error) {
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
#[error("Failed to download and build `{sdist}`")]
#[diagnostic()]
struct Error {
sdist: Box<SourceDist>,
#[source]
cause: uv_distribution::Error,
#[help]
help: Option<String>,
}

let report = miette::Report::new(Error {
help: SUGGESTIONS.get(sdist.name()).map(|suggestion| {
format!(
"`{}` is often confused for `{}` Did you mean to install `{}` instead?",
sdist.name().cyan(),
suggestion.cyan(),
suggestion.cyan(),
)
}),
sdist,
cause,
});
anstream::eprint!("{report:?}");
}

/// Render a [`uv_resolver::ResolveError::Build`] with a help message.
pub(crate) fn build(sdist: Box<SourceDist>, cause: uv_distribution::Error) {
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
#[error("Failed to build `{sdist}`")]
#[diagnostic()]
struct Error {
sdist: Box<SourceDist>,
#[source]
cause: uv_distribution::Error,
#[help]
help: Option<String>,
}

let report = miette::Report::new(Error {
help: SUGGESTIONS.get(sdist.name()).map(|suggestion| {
format!(
"`{}` is often confused for `{}` Did you mean to install `{}` instead?",
sdist.name().cyan(),
suggestion.cyan(),
suggestion.cyan(),
)
}),
sdist,
cause,
});
anstream::eprint!("{report:?}");
}

/// Render a [`uv_resolver::NoSolutionError`].
pub(crate) fn no_solution(err: &uv_resolver::NoSolutionError) {
let report = miette::Report::msg(format!("{err}")).context(err.header());
anstream::eprint!("{report:?}");
}

/// Render a [`uv_resolver::NoSolutionError`] with dedicated context.
pub(crate) fn no_solution_context(err: &uv_resolver::NoSolutionError, context: &'static str) {
let report = miette::Report::msg(format!("{err}")).context(err.header().with_context(context));
anstream::eprint!("{report:?}");
}

/// 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)]
#[error("{header}")]
#[diagnostic()]
struct Error {
/// The header to render in the error message.
header: uv_resolver::NoSolutionHeader,

/// The underlying error.
#[source]
err: uv_resolver::NoSolutionError,

/// The help message to display.
#[help]
help: String,
}

let header = err.header();
let report = miette::Report::new(Error { header, err, help });
anstream::eprint!("{report:?}");
}
1 change: 1 addition & 0 deletions crates/uv/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub(crate) mod build_backend;
mod cache_clean;
mod cache_dir;
mod cache_prune;
mod diagnostics;
mod help;
pub(crate) mod pip;
mod project;
Expand Down
14 changes: 10 additions & 4 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::env;
use std::path::Path;

use anstream::eprint;
use anyhow::{anyhow, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
Expand Down Expand Up @@ -42,7 +41,7 @@ use uv_warnings::warn_user;

use crate::commands::pip::loggers::DefaultResolveLogger;
use crate::commands::pip::{operations, resolution_environment};
use crate::commands::{ExitStatus, OutputWriter};
use crate::commands::{diagnostics, ExitStatus, OutputWriter};
use crate::printer::Printer;

/// Resolve a set of requirements into a set of pinned versions.
Expand Down Expand Up @@ -391,8 +390,15 @@ pub(crate) async fn pip_compile(
{
Ok(resolution) => resolution,
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
let report = miette::Report::msg(format!("{err}")).context(err.header());
eprint!("{report:?}");
diagnostics::no_solution(&err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::FetchAndBuild(dist, err))) => {
diagnostics::fetch_and_build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::Build(dist, err))) => {
diagnostics::build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(err) => return Err(err.into()),
Expand Down
14 changes: 10 additions & 4 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::fmt::Write;

use anstream::eprint;
use itertools::Itertools;
use owo_colors::OwoColorize;
use tracing::{debug, enabled, Level};
Expand Down Expand Up @@ -36,7 +35,7 @@ use uv_types::{BuildIsolation, HashStrategy};
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
use crate::commands::pip::operations::Modifications;
use crate::commands::pip::{operations, resolution_markers, resolution_tags};
use crate::commands::{ExitStatus, SharedState};
use crate::commands::{diagnostics, ExitStatus, SharedState};
use crate::printer::Printer;

/// Install packages into the current environment.
Expand Down Expand Up @@ -394,8 +393,15 @@ pub(crate) async fn pip_install(
{
Ok(resolution) => Resolution::from(resolution),
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
let report = miette::Report::msg(format!("{err}")).context(err.header());
eprint!("{report:?}");
diagnostics::no_solution(&err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::FetchAndBuild(dist, err))) => {
diagnostics::fetch_and_build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::Build(dist, err))) => {
diagnostics::build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(err) => return Err(err.into()),
Expand Down
14 changes: 10 additions & 4 deletions crates/uv/src/commands/pip/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::fmt::Write;

use anstream::eprint;
use anyhow::Result;
use owo_colors::OwoColorize;
use tracing::debug;
Expand Down Expand Up @@ -32,7 +31,7 @@ use uv_types::{BuildIsolation, HashStrategy};
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
use crate::commands::pip::operations::Modifications;
use crate::commands::pip::{operations, resolution_markers, resolution_tags};
use crate::commands::{ExitStatus, SharedState};
use crate::commands::{diagnostics, ExitStatus, SharedState};
use crate::printer::Printer;

/// Install a set of locked requirements into the current Python environment.
Expand Down Expand Up @@ -345,8 +344,15 @@ pub(crate) async fn pip_sync(
{
Ok(resolution) => Resolution::from(resolution),
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
let report = miette::Report::msg(format!("{err}")).context(err.header());
eprint!("{report:?}");
diagnostics::no_solution(&err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::FetchAndBuild(dist, err))) => {
diagnostics::fetch_and_build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(operations::Error::Resolve(uv_resolver::ResolveError::Build(dist, err))) => {
diagnostics::build(dist, err);
return Ok(ExitStatus::Failure);
}
Err(err) => return Err(err.into()),
Expand Down
Loading