From 8c6b667eebd86191b1304d232f328063378b3fee Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 7 Aug 2024 14:09:10 -0400 Subject: [PATCH] Add `uv add --no-sync` and `uv remove --no-sync` (#5881) ## Summary Closes https://github.com/astral-sh/uv/issues/5867. --- crates/uv-cli/src/lib.rs | 8 ++++ crates/uv/src/commands/project/add.rs | 5 +++ crates/uv/src/commands/project/remove.rs | 6 +++ crates/uv/src/lib.rs | 2 + crates/uv/src/settings.rs | 6 +++ crates/uv/tests/edit.rs | 49 ++++++++++++++++++++++++ 6 files changed, 76 insertions(+) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index fe6b74fa5..129639afd 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2304,6 +2304,10 @@ pub struct AddArgs { #[arg(long)] pub extra: Option>, + /// Avoid syncing the virtual environment after re-locking the project. + #[arg(long, conflicts_with = "frozen")] + pub no_sync: bool, + /// Assert that the `uv.lock` will remain unchanged. #[arg(long, conflicts_with = "frozen")] pub locked: bool, @@ -2354,6 +2358,10 @@ pub struct RemoveArgs { #[arg(long, conflicts_with("dev"))] pub optional: Option, + /// Avoid syncing the virtual environment after re-locking the project. + #[arg(long, conflicts_with = "frozen")] + pub no_sync: bool, + /// Assert that the `uv.lock` will remain unchanged. #[arg(long, conflicts_with = "frozen")] pub locked: bool, diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 6f5ffafa0..0942a3055 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -37,6 +37,7 @@ use crate::settings::ResolverInstallerSettings; pub(crate) async fn add( locked: bool, frozen: bool, + no_sync: bool, requirements: Vec, editable: Option, dependency_type: DependencyType, @@ -364,6 +365,10 @@ pub(crate) async fn add( } } + if no_sync { + return Ok(ExitStatus::Success); + } + // Sync the environment. let (extras, dev) = match dependency_type { DependencyType::Production => { diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index 60d634f0e..c6ff75ad0 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -17,9 +17,11 @@ use crate::printer::Printer; use crate::settings::ResolverInstallerSettings; /// Remove one or more packages from the project requirements. +#[allow(clippy::fn_params_excessive_bools)] pub(crate) async fn remove( locked: bool, frozen: bool, + no_sync: bool, requirements: Vec, dependency_type: DependencyType, package: Option, @@ -120,6 +122,10 @@ pub(crate) async fn remove( ) .await?; + if no_sync { + return Ok(ExitStatus::Success); + } + // Perform a full sync, because we don't know what exactly is affected by the removal. // TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here? let extras = ExtrasSpecification::All; diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 6b8a6473a..199cf1e54 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1068,6 +1068,7 @@ async fn run_project( commands::add( args.locked, args.frozen, + args.no_sync, args.requirements, args.editable, args.dependency_type, @@ -1103,6 +1104,7 @@ async fn run_project( commands::remove( args.locked, args.frozen, + args.no_sync, args.requirements, args.dependency_type, args.package, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index ce581300e..95c06dbc3 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -639,6 +639,7 @@ impl LockSettings { pub(crate) struct AddSettings { pub(crate) locked: bool, pub(crate) frozen: bool, + pub(crate) no_sync: bool, pub(crate) requirements: Vec, pub(crate) dependency_type: DependencyType, pub(crate) editable: Option, @@ -668,6 +669,7 @@ impl AddSettings { rev, tag, branch, + no_sync, locked, frozen, installer, @@ -693,6 +695,7 @@ impl AddSettings { Self { locked, frozen, + no_sync, requirements, dependency_type, raw_sources, @@ -718,6 +721,7 @@ impl AddSettings { pub(crate) struct RemoveSettings { pub(crate) locked: bool, pub(crate) frozen: bool, + pub(crate) no_sync: bool, pub(crate) requirements: Vec, pub(crate) dependency_type: DependencyType, pub(crate) package: Option, @@ -734,6 +738,7 @@ impl RemoveSettings { dev, optional, requirements, + no_sync, locked, frozen, installer, @@ -754,6 +759,7 @@ impl RemoveSettings { Self { locked, frozen, + no_sync, requirements, dependency_type, package, diff --git a/crates/uv/tests/edit.rs b/crates/uv/tests/edit.rs index 425a8ec58..ac899d52b 100644 --- a/crates/uv/tests/edit.rs +++ b/crates/uv/tests/edit.rs @@ -2136,6 +2136,55 @@ fn add_frozen() -> Result<()> { Ok(()) } +/// Add a requirement without updating the environment. +#[test] +fn add_no_sync() -> 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 = [] + "#})?; + + uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--no-sync"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv add` is experimental and may change without warning + Resolved 4 packages in [TIME] + "###); + + let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("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 = [ + "anyio==3.7.0", + ] + "### + ); + }); + + assert!(context.temp_dir.join("uv.lock").exists()); + + Ok(()) +} + #[test] fn add_reject_multiple_git_ref_flags() { let context = TestContext::new("3.12");