From 0fcccb89949c5835777fde7284b44b6d9c879d83 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 7 Jan 2025 08:47:05 -0500 Subject: [PATCH] Accept requirements in `uv remove` (#10338) ## Summary This allows, e.g., `uv remove flask[dotenv]` to remove `flask`. Like `pip install` and `uv pip install`, the content after the package name has no effect. Closes https://github.com/astral-sh/uv/issues/9764. --- crates/uv-cli/src/lib.rs | 2 +- crates/uv-settings/src/settings.rs | 4 ++- crates/uv/src/settings.rs | 6 ++++ crates/uv/tests/it/edit.rs | 53 ++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index ade2d43ac..6419f3213 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -3272,7 +3272,7 @@ pub struct AddArgs { pub struct RemoveArgs { /// The names of the dependencies to remove (e.g., `ruff`). #[arg(required = true)] - pub packages: Vec, + pub packages: Vec>, /// Remove the packages from the development dependency group. /// diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 6f645ebc6..6dd61ea38 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -1,6 +1,8 @@ -use serde::{Deserialize, Serialize}; use std::{fmt::Debug, num::NonZeroUsize, path::PathBuf}; + +use serde::{Deserialize, Serialize}; use url::Url; + use uv_cache_info::CacheKey; use uv_configuration::{ ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, RequiredVersion, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index fff0dabb6..dd327cf66 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -5,6 +5,7 @@ use std::process; use std::str::FromStr; use url::Url; + use uv_cache::{CacheArgs, Refresh}; use uv_cli::comma::CommaSeparatedRequirements; use uv_cli::{ @@ -1251,6 +1252,11 @@ impl RemoveSettings { .map(|fs| fs.install_mirrors.clone()) .unwrap_or_default(); + let packages = packages + .into_iter() + .map(|requirement| requirement.name) + .collect(); + Self { locked, frozen, diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index 9deb60390..aabfa2435 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -8386,3 +8386,56 @@ fn add_no_indent() -> Result<()> { }); Ok(()) } + +/// Accept requirements, not just package names, in `uv remove`. +#[test] +fn remove_requirement() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str(indoc! {r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["flask"] + + [build-system] + requires = ["setuptools>=42"] + build-backend = "setuptools.build_meta" + "#})?; + + uv_snapshot!(context.filters(), context.remove().arg("flask[dotenv]"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + project==0.1.0 (from file://[TEMP_DIR]/) + "###); + + let pyproject_toml = context.read("pyproject.toml"); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + pyproject_toml, @r###" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = [] + + [build-system] + requires = ["setuptools>=42"] + build-backend = "setuptools.build_meta" + "### + ); + }); + + Ok(()) +}