mirror of https://github.com/astral-sh/uv
feat: add comma value-delimiter to with argument in tool run args to allow for multiple arguments in with flag (#7909)
This is to address my own issue #7908 ## Summary This change makes use of the `clap` value_delimiter parser to populate the `with` `Vec<String>` which currently can either only be empty or with 1 value for each `--with` flag. This makes use of the current code structure but allows for multiple arguments with a single `--with` flag. <!-- What's the purpose of the change? What does it do, and why? --> ## Test Plan Can be tested with the following CLI: ```bash target/debug/uv tool run --with numpy,polars,matplotlib ipython -c "import numpy;import polars;import matplotlib;" ``` And former behavior of multiple `--with` flags are kept ```bash target/debug/uv tool run --with numpy --with polars --with matplotlib ipython -c "import numpy;import polars;import matplotlib;" ``` <!-- How was it tested? --> --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
parent
2506c1c274
commit
7bd0d97ce5
|
|
@ -2571,7 +2571,7 @@ pub struct RunArgs {
|
||||||
/// When used in a project, these dependencies will be layered on top of
|
/// When used in a project, these dependencies will be layered on top of
|
||||||
/// the project environment in a separate, ephemeral environment. These
|
/// the project environment in a separate, ephemeral environment. These
|
||||||
/// dependencies are allowed to conflict with those specified by the project.
|
/// dependencies are allowed to conflict with those specified by the project.
|
||||||
#[arg(long)]
|
#[arg(long, value_delimiter = ',')]
|
||||||
pub with: Vec<String>,
|
pub with: Vec<String>,
|
||||||
|
|
||||||
/// Run with the given packages installed as editables.
|
/// Run with the given packages installed as editables.
|
||||||
|
|
@ -2579,7 +2579,7 @@ pub struct RunArgs {
|
||||||
/// When used in a project, these dependencies will be layered on top of
|
/// When used in a project, these dependencies will be layered on top of
|
||||||
/// the project environment in a separate, ephemeral environment. These
|
/// the project environment in a separate, ephemeral environment. These
|
||||||
/// dependencies are allowed to conflict with those specified by the project.
|
/// dependencies are allowed to conflict with those specified by the project.
|
||||||
#[arg(long)]
|
#[arg(long, value_delimiter = ',')]
|
||||||
pub with_editable: Vec<String>,
|
pub with_editable: Vec<String>,
|
||||||
|
|
||||||
/// Run with all packages listed in the given `requirements.txt` files.
|
/// Run with all packages listed in the given `requirements.txt` files.
|
||||||
|
|
@ -2587,7 +2587,7 @@ pub struct RunArgs {
|
||||||
/// The same environment semantics as `--with` apply.
|
/// The same environment semantics as `--with` apply.
|
||||||
///
|
///
|
||||||
/// Using `pyproject.toml`, `setup.py`, or `setup.cfg` files is not allowed.
|
/// Using `pyproject.toml`, `setup.py`, or `setup.cfg` files is not allowed.
|
||||||
#[arg(long, value_parser = parse_maybe_file_path)]
|
#[arg(long, value_delimiter = ',', value_parser = parse_maybe_file_path)]
|
||||||
pub with_requirements: Vec<Maybe<PathBuf>>,
|
pub with_requirements: Vec<Maybe<PathBuf>>,
|
||||||
|
|
||||||
/// Run the command in an isolated virtual environment.
|
/// Run the command in an isolated virtual environment.
|
||||||
|
|
@ -3373,7 +3373,7 @@ pub struct ToolRunArgs {
|
||||||
pub from: Option<String>,
|
pub from: Option<String>,
|
||||||
|
|
||||||
/// Run with the given packages installed.
|
/// Run with the given packages installed.
|
||||||
#[arg(long)]
|
#[arg(long, value_delimiter = ',')]
|
||||||
pub with: Vec<String>,
|
pub with: Vec<String>,
|
||||||
|
|
||||||
/// Run with the given packages installed as editables
|
/// Run with the given packages installed as editables
|
||||||
|
|
@ -3381,11 +3381,11 @@ pub struct ToolRunArgs {
|
||||||
/// When used in a project, these dependencies will be layered on top of
|
/// When used in a project, these dependencies will be layered on top of
|
||||||
/// the uv tool's environment in a separate, ephemeral environment. These
|
/// the uv tool's environment in a separate, ephemeral environment. These
|
||||||
/// dependencies are allowed to conflict with those specified.
|
/// dependencies are allowed to conflict with those specified.
|
||||||
#[arg(long)]
|
#[arg(long, value_delimiter = ',')]
|
||||||
pub with_editable: Vec<String>,
|
pub with_editable: Vec<String>,
|
||||||
|
|
||||||
/// Run with all packages listed in the given `requirements.txt` files.
|
/// Run with all packages listed in the given `requirements.txt` files.
|
||||||
#[arg(long, value_parser = parse_maybe_file_path)]
|
#[arg(long, value_delimiter = ',', value_parser = parse_maybe_file_path)]
|
||||||
pub with_requirements: Vec<Maybe<PathBuf>>,
|
pub with_requirements: Vec<Maybe<PathBuf>>,
|
||||||
|
|
||||||
/// Run the tool in an isolated virtual environment, ignoring any already-installed tools.
|
/// Run the tool in an isolated virtual environment, ignoring any already-installed tools.
|
||||||
|
|
@ -3441,11 +3441,11 @@ pub struct ToolInstallArgs {
|
||||||
pub from: Option<String>,
|
pub from: Option<String>,
|
||||||
|
|
||||||
/// Include the following extra requirements.
|
/// Include the following extra requirements.
|
||||||
#[arg(long)]
|
#[arg(long, value_delimiter = ',')]
|
||||||
pub with: Vec<String>,
|
pub with: Vec<String>,
|
||||||
|
|
||||||
/// Run all requirements listed in the given `requirements.txt` files.
|
/// Run all requirements listed in the given `requirements.txt` files.
|
||||||
#[arg(long, value_parser = parse_maybe_file_path)]
|
#[arg(long, value_delimiter = ',', value_parser = parse_maybe_file_path)]
|
||||||
pub with_requirements: Vec<Maybe<PathBuf>>,
|
pub with_requirements: Vec<Maybe<PathBuf>>,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
|
|
|
||||||
|
|
@ -829,6 +829,324 @@ fn tool_run_without_output() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn tool_run_csv_with() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let black_editable = context.temp_dir.child("src").child("black_editable");
|
||||||
|
copy_dir_all(
|
||||||
|
context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/packages/black_editable"),
|
||||||
|
&black_editable,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import sniffio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// performs a tool run with CSV `with` flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg("numpy,pandas")
|
||||||
|
.arg("ipython")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import numpy; import pandas;")
|
||||||
|
.env("UV_TOOL_DIR", tool_dir.as_os_str())
|
||||||
|
.env("XDG_BIN_HOME", 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]
|
||||||
|
+ asttokens==2.4.1
|
||||||
|
+ decorator==5.1.1
|
||||||
|
+ executing==2.0.1
|
||||||
|
+ ipython==8.22.2
|
||||||
|
+ jedi==0.19.1
|
||||||
|
+ matplotlib-inline==0.1.6
|
||||||
|
+ numpy==1.26.4
|
||||||
|
+ pandas==2.2.1
|
||||||
|
+ parso==0.8.3
|
||||||
|
+ pexpect==4.9.0
|
||||||
|
+ prompt-toolkit==3.0.43
|
||||||
|
+ ptyprocess==0.7.0
|
||||||
|
+ pure-eval==0.2.2
|
||||||
|
+ pygments==2.17.2
|
||||||
|
+ python-dateutil==2.9.0.post0
|
||||||
|
+ pytz==2024.1
|
||||||
|
+ six==1.16.0
|
||||||
|
+ stack-data==0.6.3
|
||||||
|
+ traitlets==5.14.2
|
||||||
|
+ tzdata==2024.1
|
||||||
|
+ wcwidth==0.2.13
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn tool_run_csv_with() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let black_editable = context.temp_dir.child("src").child("black_editable");
|
||||||
|
copy_dir_all(
|
||||||
|
context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/packages/black_editable"),
|
||||||
|
&black_editable,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import sniffio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// performs a tool run with CSV `with` flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg("numpy,pandas")
|
||||||
|
.arg("ipython")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import numpy; import pandas;")
|
||||||
|
.env("UV_TOOL_DIR", tool_dir.as_os_str())
|
||||||
|
.env("XDG_BIN_HOME", 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]
|
||||||
|
+ asttokens==2.4.1
|
||||||
|
+ decorator==5.1.1
|
||||||
|
+ executing==2.0.1
|
||||||
|
+ ipython==8.22.2
|
||||||
|
+ jedi==0.19.1
|
||||||
|
+ matplotlib-inline==0.1.6
|
||||||
|
+ numpy==1.26.4
|
||||||
|
+ pandas==2.2.1
|
||||||
|
+ parso==0.8.3
|
||||||
|
+ prompt-toolkit==3.0.43
|
||||||
|
+ pure-eval==0.2.2
|
||||||
|
+ pygments==2.17.2
|
||||||
|
+ python-dateutil==2.9.0.post0
|
||||||
|
+ pytz==2024.1
|
||||||
|
+ six==1.16.0
|
||||||
|
+ stack-data==0.6.3
|
||||||
|
+ traitlets==5.14.2
|
||||||
|
+ wcwidth==0.2.13
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn tool_run_repeated_with() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let black_editable = context.temp_dir.child("src").child("black_editable");
|
||||||
|
copy_dir_all(
|
||||||
|
context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/packages/black_editable"),
|
||||||
|
&black_editable,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import sniffio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// performs a tool run with repeated `with` flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg("numpy")
|
||||||
|
.arg("--with")
|
||||||
|
.arg("pandas")
|
||||||
|
.arg("ipython")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import numpy; import pandas;")
|
||||||
|
.env("UV_TOOL_DIR", tool_dir.as_os_str())
|
||||||
|
.env("XDG_BIN_HOME", 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]
|
||||||
|
+ asttokens==2.4.1
|
||||||
|
+ decorator==5.1.1
|
||||||
|
+ executing==2.0.1
|
||||||
|
+ ipython==8.22.2
|
||||||
|
+ jedi==0.19.1
|
||||||
|
+ matplotlib-inline==0.1.6
|
||||||
|
+ numpy==1.26.4
|
||||||
|
+ pandas==2.2.1
|
||||||
|
+ parso==0.8.3
|
||||||
|
+ pexpect==4.9.0
|
||||||
|
+ prompt-toolkit==3.0.43
|
||||||
|
+ ptyprocess==0.7.0
|
||||||
|
+ pure-eval==0.2.2
|
||||||
|
+ pygments==2.17.2
|
||||||
|
+ python-dateutil==2.9.0.post0
|
||||||
|
+ pytz==2024.1
|
||||||
|
+ six==1.16.0
|
||||||
|
+ stack-data==0.6.3
|
||||||
|
+ traitlets==5.14.2
|
||||||
|
+ tzdata==2024.1
|
||||||
|
+ wcwidth==0.2.13
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn tool_run_repeated_with() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let black_editable = context.temp_dir.child("src").child("black_editable");
|
||||||
|
copy_dir_all(
|
||||||
|
context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/packages/black_editable"),
|
||||||
|
&black_editable,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["anyio", "sniffio==1.3.1"]
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import sniffio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// performs a tool run with repeated `with` flag
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg("numpy")
|
||||||
|
.arg("--with")
|
||||||
|
.arg("pandas")
|
||||||
|
.arg("ipython")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import numpy; import pandas;")
|
||||||
|
.env("UV_TOOL_DIR", tool_dir.as_os_str())
|
||||||
|
.env("XDG_BIN_HOME", 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]
|
||||||
|
+ asttokens==2.4.1
|
||||||
|
+ decorator==5.1.1
|
||||||
|
+ executing==2.0.1
|
||||||
|
+ ipython==8.22.2
|
||||||
|
+ jedi==0.19.1
|
||||||
|
+ matplotlib-inline==0.1.6
|
||||||
|
+ numpy==1.26.4
|
||||||
|
+ pandas==2.2.1
|
||||||
|
+ parso==0.8.3
|
||||||
|
+ prompt-toolkit==3.0.43
|
||||||
|
+ pure-eval==0.2.2
|
||||||
|
+ pygments==2.17.2
|
||||||
|
+ python-dateutil==2.9.0.post0
|
||||||
|
+ pytz==2024.1
|
||||||
|
+ six==1.16.0
|
||||||
|
+ stack-data==0.6.3
|
||||||
|
+ traitlets==5.14.2
|
||||||
|
+ wcwidth==0.2.13
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tool_run_with_editable() -> anyhow::Result<()> {
|
fn tool_run_with_editable() -> anyhow::Result<()> {
|
||||||
let context = TestContext::new("3.12").with_filtered_counts();
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue