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.
This commit is contained in:
Charlie Marsh 2025-01-07 08:47:05 -05:00 committed by GitHub
parent 788df24460
commit 0fcccb8994
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 2 deletions

View File

@ -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<PackageName>,
pub packages: Vec<Requirement<VerbatimParsedUrl>>,
/// Remove the packages from the development dependency group.
///

View File

@ -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,

View File

@ -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,

View File

@ -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(())
}