mirror of https://github.com/astral-sh/uv
Add a custom suggestion for `uv add dotenv` (#7799)
## Summary This was brought up on Twitter recently. `dotenv` hasn't been updated in years and doesn't build successfully anymore. Users almost always mean to install `python-dotenv`. I think we can add helpful hints here to point users in the right direction. ## Test Plan 
This commit is contained in:
parent
da9e85cc6a
commit
71d5661bd8
|
|
@ -4440,6 +4440,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"cache-key",
|
||||
"clap",
|
||||
"console",
|
||||
"ctrlc",
|
||||
"distribution-filename",
|
||||
"distribution-types",
|
||||
|
|
@ -4488,6 +4489,7 @@ dependencies = [
|
|||
"uv-cli",
|
||||
"uv-client",
|
||||
"uv-configuration",
|
||||
"uv-console",
|
||||
"uv-dispatch",
|
||||
"uv-distribution",
|
||||
"uv-extract",
|
||||
|
|
@ -4707,6 +4709,14 @@ dependencies = [
|
|||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-console"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"console",
|
||||
"ctrlc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-dev"
|
||||
version = "0.0.1"
|
||||
|
|
@ -5078,7 +5088,6 @@ dependencies = [
|
|||
"cache-key",
|
||||
"configparser",
|
||||
"console",
|
||||
"ctrlc",
|
||||
"distribution-filename",
|
||||
"distribution-types",
|
||||
"fs-err",
|
||||
|
|
@ -5094,6 +5103,7 @@ dependencies = [
|
|||
"url",
|
||||
"uv-client",
|
||||
"uv-configuration",
|
||||
"uv-console",
|
||||
"uv-distribution",
|
||||
"uv-fs",
|
||||
"uv-git",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ uv-cache-info = { path = "crates/uv-cache-info" }
|
|||
uv-cli = { path = "crates/uv-cli" }
|
||||
uv-client = { path = "crates/uv-client" }
|
||||
uv-configuration = { path = "crates/uv-configuration" }
|
||||
uv-console = { path = "crates/uv-console" }
|
||||
uv-dispatch = { path = "crates/uv-dispatch" }
|
||||
uv-distribution = { path = "crates/uv-distribution" }
|
||||
uv-extract = { path = "crates/uv-extract" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "uv-console"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
description = "Utilities for interacting with the terminal"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
ctrlc = { workspace = true }
|
||||
console = { workspace = true }
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
use anyhow::Result;
|
||||
use console::{style, Key, Term};
|
||||
|
||||
/// Prompt the user for confirmation in the given [`Term`].
|
||||
///
|
||||
/// This is a slimmed-down version of `dialoguer::Confirm`, with the post-confirmation report
|
||||
/// enabled.
|
||||
pub(crate) fn confirm(message: &str, term: &Term, default: bool) -> Result<bool> {
|
||||
pub fn confirm(message: &str, term: &Term, default: bool) -> std::io::Result<bool> {
|
||||
// Set the Ctrl-C handler to exit the process.
|
||||
let result = ctrlc::set_handler(move || {
|
||||
let term = Term::stderr();
|
||||
|
|
@ -26,7 +25,7 @@ pub(crate) fn confirm(message: &str, term: &Term, default: bool) -> Result<bool>
|
|||
// If multiple handlers were set, we assume that the existing handler is our
|
||||
// confirmation handler, and continue.
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
Err(err) => return Err(std::io::Error::new(std::io::ErrorKind::Other, err)),
|
||||
}
|
||||
|
||||
let prompt = format!(
|
||||
|
|
@ -21,6 +21,7 @@ pypi-types = { workspace = true }
|
|||
requirements-txt = { workspace = true, features = ["http"] }
|
||||
uv-client = { workspace = true }
|
||||
uv-configuration = { workspace = true }
|
||||
uv-console = { workspace = true }
|
||||
uv-distribution = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-git = { workspace = true }
|
||||
|
|
@ -33,7 +34,6 @@ uv-workspace = { workspace = true }
|
|||
anyhow = { workspace = true }
|
||||
configparser = { workspace = true }
|
||||
console = { workspace = true }
|
||||
ctrlc = { workspace = true }
|
||||
fs-err = { workspace = true, features = ["tokio"] }
|
||||
futures = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ pub use crate::sources::*;
|
|||
pub use crate::specification::*;
|
||||
pub use crate::unnamed::*;
|
||||
|
||||
mod confirm;
|
||||
mod lookahead;
|
||||
mod source_tree;
|
||||
mod sources;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ use console::Term;
|
|||
use uv_fs::Simplified;
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::confirm;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RequirementsSource {
|
||||
/// A package was provided on the command line (e.g., `pip install flask`).
|
||||
|
|
@ -96,7 +94,7 @@ impl RequirementsSource {
|
|||
let prompt = format!(
|
||||
"`{name}` looks like a local requirements file but was passed as a package name. Did you mean `-r {name}`?"
|
||||
);
|
||||
let confirmation = confirm::confirm(&prompt, &term, true).unwrap();
|
||||
let confirmation = uv_console::confirm(&prompt, &term, true).unwrap();
|
||||
if confirmation {
|
||||
return Self::from_requirements_file(name.into());
|
||||
}
|
||||
|
|
@ -113,7 +111,7 @@ impl RequirementsSource {
|
|||
let prompt = format!(
|
||||
"`{name}` looks like a local metadata file but was passed as a package name. Did you mean `-r {name}`?"
|
||||
);
|
||||
let confirmation = confirm::confirm(&prompt, &term, true).unwrap();
|
||||
let confirmation = uv_console::confirm(&prompt, &term, true).unwrap();
|
||||
if confirmation {
|
||||
return Self::from_requirements_file(name.into());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ 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 }
|
||||
|
|
@ -57,6 +58,7 @@ 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"] }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::fmt::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use console::Term;
|
||||
use itertools::Itertools;
|
||||
use owo_colors::OwoColorize;
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::fmt::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use tracing::debug;
|
||||
|
||||
use cache_key::RepositoryUrl;
|
||||
|
|
@ -48,6 +50,18 @@ use crate::commands::{pip, project, ExitStatus, SharedState};
|
|||
use crate::printer::Printer;
|
||||
use crate::settings::{ResolverInstallerSettings, ResolverInstallerSettingsRef};
|
||||
|
||||
static CORRECTIONS: LazyLock<FxHashMap<PackageName, PackageName>> = LazyLock::new(|| {
|
||||
[("dotenv", "python-dotenv"), ("sklearn", "scikit-learn")]
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
PackageName::from_str(k).unwrap(),
|
||||
PackageName::from_str(v).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
/// Add one or more packages to the project requirements.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub(crate) async fn add(
|
||||
|
|
@ -371,6 +385,23 @@ pub(crate) async fn add(
|
|||
}?;
|
||||
let mut edits = Vec::<DependencyEdit>::with_capacity(requirements.len());
|
||||
for mut requirement in requirements {
|
||||
// If the user requested a package that is often confused for another package, prompt them.
|
||||
if let Some(correction) = CORRECTIONS.get(&requirement.name) {
|
||||
let term = Term::stderr();
|
||||
if term.is_term() {
|
||||
let prompt = format!(
|
||||
"`{}` is often confused for `{}`. Did you mean `{}`?",
|
||||
requirement.name.cyan(),
|
||||
correction.cyan(),
|
||||
format!("uv add {correction}").green()
|
||||
);
|
||||
let confirmation = uv_console::confirm(&prompt, &term, true)?;
|
||||
if confirmation {
|
||||
requirement.name = correction.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the specified extras.
|
||||
requirement.extras.extend(extras.iter().cloned());
|
||||
requirement.extras.sort_unstable();
|
||||
|
|
|
|||
Loading…
Reference in New Issue