Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uv sync --no-install-package to skip installation of specific packages #6540

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,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![];

if let Err(err) = 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_project = 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_project,
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 @@ -411,14 +411,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
20 changes: 20 additions & 0 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{Context, Result};
use distribution_types::Name;
use itertools::Itertools;
use pep508_rs::MarkerTree;
use rustc_hash::FxHashSet;
use tracing::debug;
use uv_auth::store_credentials_from_url;
use uv_cache::Cache;
Expand Down Expand Up @@ -34,6 +35,7 @@ pub(crate) async fn sync(
dev: bool,
no_install_project: bool,
no_install_workspace: bool,
no_install_package: Vec<PackageName>,
modifications: Modifications,
python: Option<String>,
python_preference: PythonPreference,
Expand Down Expand Up @@ -108,6 +110,7 @@ pub(crate) async fn sync(
dev,
no_install_project,
no_install_workspace,
no_install_package,
modifications,
settings.as_ref().into(),
&state,
Expand All @@ -133,6 +136,7 @@ pub(super) async fn do_sync(
dev: bool,
no_install_project: bool,
no_install_workspace: bool,
no_install_package: Vec<PackageName>,
modifications: Modifications,
settings: InstallerSettingsRef<'_>,
state: &SharedState,
Expand Down Expand Up @@ -202,6 +206,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);

// Add all authenticated sources to the cache.
for url in index_locations.urls() {
store_credentials_from_url(url);
Expand Down Expand Up @@ -321,3 +328,16 @@ 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: &[PackageName],
resolution: distribution_types::Resolution,
) -> distribution_types::Resolution {
if no_install_package.is_empty() {
return resolution;
}

let no_install_packages = no_install_package.iter().collect::<FxHashSet<_>>();

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 @@ -1104,6 +1104,7 @@ async fn run_project(
args.dev,
args.no_install_project,
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 @@ -619,6 +619,7 @@ pub(crate) struct SyncSettings {
pub(crate) dev: bool,
pub(crate) no_install_project: 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 @@ -640,6 +641,7 @@ impl SyncSettings {
exact,
no_install_project,
no_install_workspace,
no_install_package,
locked,
frozen,
installer,
Expand Down Expand Up @@ -674,6 +676,7 @@ impl SyncSettings {
dev: flag(dev, no_dev).unwrap_or(true),
no_install_project,
no_install_workspace,
no_install_package,
modifications,
package,
python,
Expand Down
57 changes: 55 additions & 2 deletions crates/uv/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -864,8 +864,8 @@ fn no_install_project() -> Result<()> {
Ok(())
}

/// Avoid syncing local dependencies for workspace dependencies when `--no-install-project` is provided, but
/// include the workspace dependency's dependencies.
/// Avoid syncing workspace members and the project when `--no-install-workspace` is provided, but
/// include the all of the dependencies.
#[test]
fn no_install_workspace() -> Result<()> {
let context = TestContext::new("3.12");
Expand Down 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(())
}
4 changes: 3 additions & 1 deletion docs/guides/integration/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,7 @@ RUN uv sync --frozen

!!! tip

If you're using a [workspace](../../concepts/workspaces.md), then consider the
If you're using a [workspace](../../concepts/workspaces.md), then use the
`--no-install-workspace` flag which excludes the project _and_ any workspace members.

If you want to remove specific packages from the sync, use `--no-install-package <name>`.
4 changes: 4 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,10 @@ uv sync [OPTIONS]

</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>

</dd><dt><code>--no-install-package</code> <i>no-install-package</i></dt><dd><p>Do not install the given package(s).</p>

<p>By default, all of the project&#8217;s dependencies are installed into the environment. The <code>--no-install-package</code> option allows exclusion of specific packages. Note this can result in a broken environment, and should be used with caution.</p>

</dd><dt><code>--no-install-project</code></dt><dd><p>Do not install the current project.</p>

<p>By default, the current project is installed into the environment with all of its dependencies. The <code>--no-install-project</code> option allows the project to be excluded, but all of its dependencies are still installed. This is particularly useful in situations like building Docker images where installing the project separately from its dependencies allows optimal layer caching.</p>
Expand Down
Loading