diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 865a04f6a195..48b874ca9571 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -3514,6 +3514,10 @@ pub struct ToolInstallArgs { #[arg(short, long)] pub editable: bool, + /// Include the given packages as editables. + #[arg(long, value_delimiter = ',')] + pub with_editable: Vec, + /// The package to install commands from. /// /// This option is provided for parity with `uv tool run`, but is redundant with `package`. diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index e9380b82872e..21e2659fda87 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -922,6 +922,11 @@ async fn run(mut cli: Cli) -> Result { .with .into_iter() .map(RequirementsSource::from_with_package) + .chain( + args.with_editable + .into_iter() + .map(RequirementsSource::Editable), + ) .chain( args.with_requirements .into_iter() diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 1b7d57088482..c9c4fc5a9faf 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -389,6 +389,7 @@ pub(crate) struct ToolInstallSettings { pub(crate) from: Option, pub(crate) with: Vec, pub(crate) with_requirements: Vec, + pub(crate) with_editable: Vec, pub(crate) python: Option, pub(crate) refresh: Refresh, pub(crate) options: ResolverInstallerOptions, @@ -406,6 +407,7 @@ impl ToolInstallSettings { editable, from, with, + with_editable, with_requirements, installer, force, @@ -427,6 +429,7 @@ impl ToolInstallSettings { package, from, with, + with_editable, with_requirements: with_requirements .into_iter() .filter_map(Maybe::into_option) diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index da2d38a32c34..ee3e89cf4e63 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -2606,6 +2606,7 @@ fn resolve_tool() -> anyhow::Result<()> { from: None, with: [], with_requirements: [], + with_editable: [], python: None, refresh: None( Timestamp( diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index c827e3bc9ebc..380e3c43238e 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -11,7 +11,7 @@ use predicates::prelude::predicate; use uv_static::EnvVars; -use crate::common::{uv_snapshot, TestContext}; +use crate::common::{copy_dir_all, uv_snapshot, TestContext}; #[test] fn tool_install() { @@ -171,6 +171,51 @@ fn tool_install() { }); } +#[test] +fn tool_install_with_editable() -> anyhow::Result<()> { + let context = TestContext::new("3.12") + .with_filtered_counts() + .with_filtered_exe_suffix(); + let tool_dir = context.temp_dir.child("tools"); + let bin_dir = context.temp_dir.child("bin"); + let anyio_local = context.temp_dir.child("src").child("anyio_local"); + copy_dir_all( + context.workspace_root.join("scripts/packages/anyio_local"), + &anyio_local, + )?; + + uv_snapshot!(context.filters(), context.tool_install() + .arg("--with-editable") + .arg("./src/anyio_local") + .arg("--with") + .arg("iniconfig") + .arg("flask") + .env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str()) + .env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()) + .env(EnvVars::PATH, bin_dir.as_os_str()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved [N] packages in [TIME] + Prepared [N] packages in [TIME] + Installed [N] packages in [TIME] + + anyio==4.3.0+foo (from file://[TEMP_DIR]/src/anyio_local) + + blinker==1.7.0 + + click==8.1.7 + + flask==3.0.2 + + iniconfig==2.0.0 + + itsdangerous==2.1.2 + + jinja2==3.1.3 + + markupsafe==2.1.5 + + werkzeug==3.0.1 + Installed 1 executable: flask + "###); + + Ok(()) +} + #[test] fn tool_install_suggest_other_packages_with_executable() { let context = TestContext::new("3.12").with_filtered_exe_suffix(); diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 143e2d5e5705..4ea817bee85b 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -3240,6 +3240,8 @@ uv tool install [OPTIONS]
--with with

Include the following extra requirements

+
--with-editable with-editable

Include the given packages as editables

+
--with-requirements with-requirements

Run all requirements listed in the given requirements.txt files