mirror of https://github.com/astral-sh/uv
Add `uv sync --no-install-package` to skip installation of specific packages (#6540)
Extends #6538 / #6539 See #4028 Allows excluding arbitrary packages from the sync.
This commit is contained in:
parent
ca50243174
commit
d1cbcb30e3
|
|
@ -2292,6 +2292,14 @@ pub struct SyncArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub no_install_workspace: bool,
|
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.
|
/// Assert that the `uv.lock` will remain unchanged.
|
||||||
///
|
///
|
||||||
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
|
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
|
||||||
|
|
|
||||||
|
|
@ -596,6 +596,9 @@ pub(crate) async fn add(
|
||||||
|
|
||||||
// Initialize any shared state.
|
// Initialize any shared state.
|
||||||
let state = SharedState::default();
|
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(
|
if let Err(err) = project::sync::do_sync(
|
||||||
&project,
|
&project,
|
||||||
|
|
@ -603,8 +606,9 @@ pub(crate) async fn add(
|
||||||
&lock,
|
&lock,
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
false,
|
no_install_root,
|
||||||
false,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
Modifications::Sufficient,
|
Modifications::Sufficient,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
&state,
|
&state,
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,7 @@ pub(crate) async fn remove(
|
||||||
let dev = true;
|
let dev = true;
|
||||||
let no_install_project = false;
|
let no_install_project = false;
|
||||||
let no_install_workspace = false;
|
let no_install_workspace = false;
|
||||||
|
let no_install_package = vec![];
|
||||||
|
|
||||||
// Initialize any shared state.
|
// Initialize any shared state.
|
||||||
let state = SharedState::default();
|
let state = SharedState::default();
|
||||||
|
|
@ -204,6 +205,7 @@ pub(crate) async fn remove(
|
||||||
dev,
|
dev,
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
Modifications::Exact,
|
Modifications::Exact,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
&state,
|
&state,
|
||||||
|
|
|
||||||
|
|
@ -411,14 +411,19 @@ pub(crate) async fn run(
|
||||||
Err(err) => return Err(err.into()),
|
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::sync::do_sync(
|
||||||
&project,
|
&project,
|
||||||
&venv,
|
&venv,
|
||||||
result.lock(),
|
result.lock(),
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
false,
|
no_install_root,
|
||||||
false,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
Modifications::Sufficient,
|
Modifications::Sufficient,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
&state,
|
&state,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
||||||
use distribution_types::Name;
|
use distribution_types::Name;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pep508_rs::MarkerTree;
|
use pep508_rs::MarkerTree;
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use uv_auth::store_credentials_from_url;
|
use uv_auth::store_credentials_from_url;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
|
|
@ -34,6 +35,7 @@ pub(crate) async fn sync(
|
||||||
dev: bool,
|
dev: bool,
|
||||||
no_install_project: bool,
|
no_install_project: bool,
|
||||||
no_install_workspace: bool,
|
no_install_workspace: bool,
|
||||||
|
no_install_package: Vec<PackageName>,
|
||||||
modifications: Modifications,
|
modifications: Modifications,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
python_preference: PythonPreference,
|
python_preference: PythonPreference,
|
||||||
|
|
@ -108,6 +110,7 @@ pub(crate) async fn sync(
|
||||||
dev,
|
dev,
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
modifications,
|
modifications,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
&state,
|
&state,
|
||||||
|
|
@ -133,6 +136,7 @@ pub(super) async fn do_sync(
|
||||||
dev: bool,
|
dev: bool,
|
||||||
no_install_project: bool,
|
no_install_project: bool,
|
||||||
no_install_workspace: bool,
|
no_install_workspace: bool,
|
||||||
|
no_install_package: Vec<PackageName>,
|
||||||
modifications: Modifications,
|
modifications: Modifications,
|
||||||
settings: InstallerSettingsRef<'_>,
|
settings: InstallerSettingsRef<'_>,
|
||||||
state: &SharedState,
|
state: &SharedState,
|
||||||
|
|
@ -202,6 +206,9 @@ pub(super) async fn do_sync(
|
||||||
// If `--no-install-workspace` is set, remove the project and any workspace members.
|
// If `--no-install-workspace` is set, remove the project and any workspace members.
|
||||||
let resolution = apply_no_install_workspace(no_install_workspace, resolution, project);
|
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.
|
// Add all authenticated sources to the cache.
|
||||||
for url in index_locations.urls() {
|
for url in index_locations.urls() {
|
||||||
store_credentials_from_url(url);
|
store_credentials_from_url(url);
|
||||||
|
|
@ -321,3 +328,16 @@ fn apply_no_install_workspace(
|
||||||
!workspace_packages.contains_key(dist.name()) && Some(dist.name()) != project.project_name()
|
!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()))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1104,6 +1104,7 @@ async fn run_project(
|
||||||
args.dev,
|
args.dev,
|
||||||
args.no_install_project,
|
args.no_install_project,
|
||||||
args.no_install_workspace,
|
args.no_install_workspace,
|
||||||
|
args.no_install_package,
|
||||||
args.modifications,
|
args.modifications,
|
||||||
args.python,
|
args.python,
|
||||||
globals.python_preference,
|
globals.python_preference,
|
||||||
|
|
|
||||||
|
|
@ -619,6 +619,7 @@ pub(crate) struct SyncSettings {
|
||||||
pub(crate) dev: bool,
|
pub(crate) dev: bool,
|
||||||
pub(crate) no_install_project: bool,
|
pub(crate) no_install_project: bool,
|
||||||
pub(crate) no_install_workspace: bool,
|
pub(crate) no_install_workspace: bool,
|
||||||
|
pub(crate) no_install_package: Vec<PackageName>,
|
||||||
pub(crate) modifications: Modifications,
|
pub(crate) modifications: Modifications,
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
pub(crate) python: Option<String>,
|
pub(crate) python: Option<String>,
|
||||||
|
|
@ -640,6 +641,7 @@ impl SyncSettings {
|
||||||
exact,
|
exact,
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
locked,
|
locked,
|
||||||
frozen,
|
frozen,
|
||||||
installer,
|
installer,
|
||||||
|
|
@ -674,6 +676,7 @@ impl SyncSettings {
|
||||||
dev: flag(dev, no_dev).unwrap_or(true),
|
dev: flag(dev, no_dev).unwrap_or(true),
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
|
no_install_package,
|
||||||
modifications,
|
modifications,
|
||||||
package,
|
package,
|
||||||
python,
|
python,
|
||||||
|
|
|
||||||
|
|
@ -864,8 +864,8 @@ fn no_install_project() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Avoid syncing local dependencies for workspace dependencies when `--no-install-project` is provided, but
|
/// Avoid syncing workspace members and the project when `--no-install-workspace` is provided, but
|
||||||
/// include the workspace dependency's dependencies.
|
/// include the all of the dependencies.
|
||||||
#[test]
|
#[test]
|
||||||
fn no_install_workspace() -> Result<()> {
|
fn no_install_workspace() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
@ -930,3 +930,56 @@ fn no_install_workspace() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,5 +219,7 @@ RUN uv sync --frozen
|
||||||
|
|
||||||
!!! tip
|
!!! 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.
|
`--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>`.
|
||||||
|
|
|
||||||
|
|
@ -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-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’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>
|
</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>
|
<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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue