Add `--no-install-*` arguments to `uv add` (#15375)

## Summary

Closes https://github.com/astral-sh/uv/issues/15369.
This commit is contained in:
Charlie Marsh 2025-08-19 15:32:56 +01:00 committed by GitHub
parent c58192eebe
commit 68f33e8fef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 138 additions and 1 deletions

View File

@ -3812,6 +3812,26 @@ pub struct AddArgs {
/// as direct path dependency instead. /// as direct path dependency instead.
#[arg(long, overrides_with = "workspace")] #[arg(long, overrides_with = "workspace")]
pub no_workspace: bool, pub no_workspace: bool,
/// Do not install the current project.
///
/// By default, the current project is installed into the environment with all of its
/// dependencies. The `--no-install-project` 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.
#[arg(long, conflicts_with = "frozen", conflicts_with = "no_sync")]
pub no_install_project: bool,
/// Do not install any workspace members, including the current project.
///
/// By default, all of the workspace members and their dependencies are installed into the
/// environment. The `--no-install-workspace` option allows exclusion of all the workspace
/// members while retaining their dependencies. This is particularly useful in situations like
/// building Docker images where installing the workspace separately from its dependencies
/// allows optimal layer caching.
#[arg(long, conflicts_with = "frozen", conflicts_with = "no_sync")]
pub no_install_workspace: bool,
} }
#[derive(Args)] #[derive(Args)]

View File

@ -69,6 +69,8 @@ pub(crate) async fn add(
frozen: bool, frozen: bool,
active: Option<bool>, active: Option<bool>,
no_sync: bool, no_sync: bool,
no_install_project: bool,
no_install_workspace: bool,
requirements: Vec<RequirementsSource>, requirements: Vec<RequirementsSource>,
constraints: Vec<RequirementsSource>, constraints: Vec<RequirementsSource>,
marker: Option<MarkerTree>, marker: Option<MarkerTree>,
@ -735,6 +737,8 @@ pub(crate) async fn add(
lock_state, lock_state,
sync_state, sync_state,
locked, locked,
no_install_project,
no_install_workspace,
&defaulted_extras, &defaulted_extras,
&defaulted_groups, &defaulted_groups,
raw, raw,
@ -963,6 +967,8 @@ async fn lock_and_sync(
lock_state: UniversalState, lock_state: UniversalState,
sync_state: PlatformState, sync_state: PlatformState,
locked: bool, locked: bool,
no_install_project: bool,
no_install_workspace: bool,
extras: &ExtrasSpecificationWithDefaults, extras: &ExtrasSpecificationWithDefaults,
groups: &DependencyGroupsWithDefaults, groups: &DependencyGroupsWithDefaults,
raw: bool, raw: bool,
@ -1149,7 +1155,7 @@ async fn lock_and_sync(
extras, extras,
groups, groups,
EditableMode::Editable, EditableMode::Editable,
InstallOptions::default(), InstallOptions::new(no_install_project, no_install_workspace, vec![]),
Modifications::Sufficient, Modifications::Sufficient,
None, None,
settings.into(), settings.into(),

View File

@ -1979,6 +1979,8 @@ async fn run_project(
args.frozen, args.frozen,
args.active, args.active,
args.no_sync, args.no_sync,
args.no_install_project,
args.no_install_workspace,
requirements, requirements,
constraints, constraints,
args.marker, args.marker,

View File

@ -1370,6 +1370,8 @@ pub(crate) struct AddSettings {
pub(crate) script: Option<PathBuf>, pub(crate) script: Option<PathBuf>,
pub(crate) python: Option<String>, pub(crate) python: Option<String>,
pub(crate) workspace: Option<bool>, pub(crate) workspace: Option<bool>,
pub(crate) no_install_project: bool,
pub(crate) no_install_workspace: bool,
pub(crate) install_mirrors: PythonInstallMirrors, pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh, pub(crate) refresh: Refresh,
pub(crate) indexes: Vec<Index>, pub(crate) indexes: Vec<Index>,
@ -1409,6 +1411,8 @@ impl AddSettings {
python, python,
workspace, workspace,
no_workspace, no_workspace,
no_install_project,
no_install_workspace,
} = args; } = args;
let dependency_type = if let Some(extra) = optional { let dependency_type = if let Some(extra) = optional {
@ -1510,6 +1514,8 @@ impl AddSettings {
script, script,
python: python.and_then(Maybe::into_option), python: python.and_then(Maybe::into_option),
workspace: flag(workspace, no_workspace, "workspace"), workspace: flag(workspace, no_workspace, "workspace"),
no_install_project,
no_install_workspace,
editable: flag(editable, no_editable, "editable"), editable: flag(editable, no_editable, "editable"),
extras: extra.unwrap_or_default(), extras: extra.unwrap_or_default(),
refresh: Refresh::from(refresh), refresh: Refresh::from(refresh),

View File

@ -13650,3 +13650,102 @@ fn add_multiline_indentation() -> Result<()> {
Ok(()) Ok(())
} }
/// Add a requirement without installing the project.
#[test]
fn add_no_install_project() -> 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 = []
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#})?;
context
.temp_dir
.child("project")
.child("src")
.child("project")
.child("__init__.py")
.touch()?;
uv_snapshot!(context.filters(), context.add().arg("iniconfig").arg("--no-install-project"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
");
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 = [
"iniconfig>=2.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#
);
});
let lock = context.read("uv.lock");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r#"
version = 1
revision = 3
requires-python = ">=3.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" },
]
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "iniconfig" },
]
[package.metadata]
requires-dist = [{ name = "iniconfig", specifier = ">=2.0.0" }]
"#
);
});
Ok(())
}

View File

@ -539,6 +539,10 @@ uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>
<p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p></dd><dt id="uv-add--no-config"><a href="#uv-add--no-config"><code>--no-config</code></a></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p> <p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p></dd><dt id="uv-add--no-config"><a href="#uv-add--no-config"><code>--no-config</code></a></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p>
<p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p> <p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p>
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p></dd><dt id="uv-add--no-index"><a href="#uv-add--no-index"><code>--no-index</code></a></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> <p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p></dd><dt id="uv-add--no-index"><a href="#uv-add--no-index"><code>--no-index</code></a></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 id="uv-add--no-install-project"><a href="#uv-add--no-install-project"><code>--no-install-project</code></a></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>
</dd><dt id="uv-add--no-install-workspace"><a href="#uv-add--no-install-workspace"><code>--no-install-workspace</code></a></dt><dd><p>Do not install any workspace members, including the current project.</p>
<p>By default, all of the workspace members and their dependencies are installed into the environment. The <code>--no-install-workspace</code> option allows exclusion of all the workspace members while retaining their dependencies. This is particularly useful in situations like building Docker images where installing the workspace separately from its dependencies allows optimal layer caching.</p>
</dd><dt id="uv-add--no-managed-python"><a href="#uv-add--no-managed-python"><code>--no-managed-python</code></a></dt><dd><p>Disable use of uv-managed Python versions.</p> </dd><dt id="uv-add--no-managed-python"><a href="#uv-add--no-managed-python"><code>--no-managed-python</code></a></dt><dd><p>Disable use of uv-managed Python versions.</p>
<p>Instead, uv will search for a suitable Python version on the system.</p> <p>Instead, uv will search for a suitable Python version on the system.</p>
<p>May also be set with the <code>UV_NO_MANAGED_PYTHON</code> environment variable.</p></dd><dt id="uv-add--no-progress"><a href="#uv-add--no-progress"><code>--no-progress</code></a></dt><dd><p>Hide all progress outputs.</p> <p>May also be set with the <code>UV_NO_MANAGED_PYTHON</code> environment variable.</p></dd><dt id="uv-add--no-progress"><a href="#uv-add--no-progress"><code>--no-progress</code></a></dt><dd><p>Hide all progress outputs.</p>