Skip to content

Commit

Permalink
Add uv sync --no-install-package to skip installation of specific p…
Browse files Browse the repository at this point in the history
…ackages
  • Loading branch information
zanieb committed Aug 23, 2024
1 parent c5e8571 commit 2dec26c
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 4 deletions.
8 changes: 8 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,14 @@ pub struct SyncArgs {
#[arg(long)]
pub no_install_workspace: bool,

/// Do not install the given package(s).
///
/// By default, all of the project's dependencies are installed into the environment. The
/// `--no-install-package` option allows exclusion of specific packages. Note this can result
/// in a broken environment, and should be used with caution.
#[arg(long)]
pub no_install_package: Vec<PackageName>,

/// Assert that the `uv.lock` will remain unchanged.
///
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
Expand Down
8 changes: 6 additions & 2 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,15 +596,19 @@ pub(crate) async fn add(

// Initialize any shared state.
let state = SharedState::default();
let no_install_root = false;
let no_install_workspace = false;
let no_install_package = vec![];

project::sync::do_sync(
&project,
&venv,
&lock,
&extras,
dev,
false,
false,
no_install_root,
no_install_workspace,
no_install_package,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ pub(crate) async fn remove(
let dev = true;
let no_install_root = false;
let no_install_workspace = false;
let no_install_package = vec![];

// Initialize any shared state.
let state = SharedState::default();
Expand All @@ -204,6 +205,7 @@ pub(crate) async fn remove(
dev,
no_install_root,
no_install_workspace,
no_install_package,
Modifications::Exact,
settings.as_ref().into(),
&state,
Expand Down
9 changes: 7 additions & 2 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,14 +413,19 @@ pub(crate) async fn run(
Err(err) => return Err(err.into()),
};

let no_install_root = false;
let no_install_workspace = false;
let no_install_package = vec![];

project::sync::do_sync(
&project,
&venv,
result.lock(),
&extras,
dev,
false,
false,
no_install_root,
no_install_workspace,
no_install_package,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
Expand Down
22 changes: 22 additions & 0 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeSet;

use anyhow::{Context, Result};
use distribution_types::Name;
use itertools::Itertools;
Expand Down Expand Up @@ -34,6 +36,7 @@ pub(crate) async fn sync(
dev: bool,
no_install_root: bool,
no_install_workspace: bool,
no_install_package: Vec<PackageName>,
modifications: Modifications,
python: Option<String>,
python_preference: PythonPreference,
Expand Down Expand Up @@ -108,6 +111,7 @@ pub(crate) async fn sync(
dev,
no_install_root,
no_install_workspace,
no_install_package,
modifications,
settings.as_ref().into(),
&state,
Expand All @@ -133,6 +137,7 @@ pub(super) async fn do_sync(
dev: bool,
no_install_root: bool,
no_install_workspace: bool,
no_install_package: Vec<PackageName>,
modifications: Modifications,
settings: InstallerSettingsRef<'_>,
state: &SharedState,
Expand Down Expand Up @@ -202,6 +207,9 @@ pub(super) async fn do_sync(
// If `--no-install-workspace` is set, remove the project and any workspace members.
let resolution = apply_no_install_workspace(no_install_workspace, resolution, project);

// If `--no-install-package` is provided, remove the requested packages.
let resolution = apply_no_install_package(no_install_package, resolution, project);

// Add all authenticated sources to the cache.
for url in index_locations.urls() {
store_credentials_from_url(url);
Expand Down Expand Up @@ -321,3 +329,17 @@ fn apply_no_install_workspace(
!workspace_packages.contains_key(dist.name()) && Some(dist.name()) != project.project_name()
})
}

fn apply_no_install_package(
no_install_package: Vec<PackageName>,
resolution: distribution_types::Resolution,
project: &VirtualProject,
) -> distribution_types::Resolution {
if no_install_package.is_empty() {
return resolution;
}

let no_install_packages = BTreeSet::from_iter(no_install_package.iter());

resolution.filter(|dist| !no_install_packages.contains(dist.name()))
}
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ async fn run_project(
args.dev,
args.no_install_root,
args.no_install_workspace,
args.no_install_package,
args.modifications,
args.python,
globals.python_preference,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ pub(crate) struct SyncSettings {
pub(crate) dev: bool,
pub(crate) no_install_root: bool,
pub(crate) no_install_workspace: bool,
pub(crate) no_install_package: Vec<PackageName>,
pub(crate) modifications: Modifications,
pub(crate) package: Option<PackageName>,
pub(crate) python: Option<String>,
Expand All @@ -641,6 +642,7 @@ impl SyncSettings {
exact,
no_install_root,
no_install_workspace,
no_install_package,
locked,
frozen,
installer,
Expand Down Expand Up @@ -675,6 +677,7 @@ impl SyncSettings {
dev: flag(dev, no_dev).unwrap_or(true),
no_install_root,
no_install_workspace,
no_install_package,
modifications,
package,
python,
Expand Down
53 changes: 53 additions & 0 deletions crates/uv/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,3 +930,56 @@ fn no_install_workspace() -> Result<()> {

Ok(())
}

/// Avoid syncing the target package when `--no-install-package` is provided.
#[test]
fn no_install_package() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"]
"#,
)?;

// Generate a lockfile.
context.lock().assert().success();

// Running with `--no-install-package anyio` should skip anyio but include everything else
uv_snapshot!(context.filters(), context.sync().arg("--no-install-package").arg("anyio"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ idna==3.6
+ project==0.1.0 (from file://[TEMP_DIR]/)
+ sniffio==1.3.1
"###);

// Running with `--no-install-package project` should skip the project itself (not as a special
// case, that's just the name of the project)
uv_snapshot!(context.filters(), context.sync().arg("--no-install-package").arg("project"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 4 packages in [TIME]
Prepared 1 package in [TIME]
Uninstalled 1 package in [TIME]
Installed 1 package in [TIME]
+ anyio==3.7.0
- project==0.1.0 (from file://[TEMP_DIR]/)
"###);

Ok(())
}

0 comments on commit 2dec26c

Please sign in to comment.