Skip to content
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
18 changes: 14 additions & 4 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ const STYLES: Styles = Styles::styled()
#[derive(Parser)]
#[command(name = "uv", author, long_version = crate::version::uv_self_version())]
#[command(about = "An extremely fast Python package manager.")]
#[command(propagate_version = true)]
#[command(
after_help = "Use `uv help` for more details.",
after_long_help = "",
Expand Down Expand Up @@ -127,7 +126,7 @@ pub struct TopLevelArgs {
help: Option<bool>,

/// Display the uv version.
#[arg(global = true, short = 'V', long, action = clap::ArgAction::Version, help_heading = "Global options")]
#[arg(short = 'V', long, action = clap::ArgAction::Version)]
version: Option<bool>,
}

Expand Down Expand Up @@ -4062,9 +4061,10 @@ pub enum ToolCommand {
override_usage = "uvx [OPTIONS] [COMMAND]",
about = "Run a command provided by a Python package.",
after_help = "Use `uv help tool run` for more details.",
after_long_help = ""
after_long_help = "",
long_version = crate::version::uv_self_version()
)]
Uvx(ToolRunArgs),
Uvx(UvxArgs),
/// Install commands provided by a Python package.
///
/// Packages are installed into an isolated virtual environment in the uv tools directory. The
Expand Down Expand Up @@ -4222,6 +4222,16 @@ pub struct ToolRunArgs {
pub generate_shell_completion: Option<clap_complete_command::Shell>,
}

#[derive(Args)]
pub struct UvxArgs {
#[command(flatten)]
pub tool_run: ToolRunArgs,

/// Display the uvx version.
#[arg(short = 'V', long, action = clap::ArgAction::Version)]
pub version: Option<bool>,
}

#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct ToolInstallArgs {
Expand Down
14 changes: 7 additions & 7 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
command: run_variant @ (ToolCommand::Uvx(_) | ToolCommand::Run(_)),
}) => {
let (args, invocation_source) = match run_variant {
ToolCommand::Uvx(args) => (args, ToolRunCommand::Uvx),
ToolCommand::Uvx(args) => (args.tool_run, ToolRunCommand::Uvx),
ToolCommand::Run(args) => (args, ToolRunCommand::ToolRun),
// OK guarded by the outer match statement
_ => unreachable!(),
Expand All @@ -1081,15 +1081,15 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.find_subcommand("uvx")
.unwrap()
.clone()
// Avoid duplicating the `--help` and `--version` flags from the top-level arguments.
// Avoid duplicating the `--help` and `--version` flags from the top-level
// arguments.
.disable_help_flag(true)
.disable_version_flag(true)
.version(env!("CARGO_PKG_VERSION"));
.disable_version_flag(true);

// Copy the top-level arguments into the `uvx` command. (Like `Args::augment_args`, but
// expanded to skip collisions.)
// Copy the top-level arguments into the `uvx` command, as in `Args::augment_args`,
// but expanded to skip collisions.
for arg in TopLevelArgs::command().get_arguments() {
if arg.get_id() != "isolated" {
if arg.get_id() != "isolated" && arg.get_id() != "version" {
uvx = uvx.arg(arg);
}
}
Expand Down
30 changes: 13 additions & 17 deletions crates/uv/tests/it/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,6 @@ fn help_subcommand() {
-h, --help
Display the concise help for this command

-V, --version
Display the uv version

Use `uv help python <command>` for more information on a specific command.


Expand Down Expand Up @@ -691,9 +688,6 @@ fn help_subsubcommand() {
-h, --help
Display the concise help for this command

-V, --version
Display the uv version


----- stderr -----
"#);
Expand Down Expand Up @@ -755,8 +749,6 @@ fn help_flag_subcommand() {
Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=]
-h, --help
Display the concise help for this command
-V, --version
Display the uv version

Use `uv help python` for more details.

Expand Down Expand Up @@ -826,8 +818,6 @@ fn help_flag_subsubcommand() {
Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=]
-h, --help
Display the concise help for this command
-V, --version
Display the uv version

----- stderr -----
"#);
Expand Down Expand Up @@ -896,7 +886,7 @@ fn help_unknown_subcommand() {
fn help_unknown_subsubcommand() {
let context = TestContext::new_with_versions(&[]);

uv_snapshot!(context.filters(), context.help().arg("python").arg("foobar"), @r###"
uv_snapshot!(context.filters(), context.help().arg("python").arg("foobar"), @r"
success: false
exit_code: 2
----- stdout -----
Expand All @@ -909,7 +899,7 @@ fn help_unknown_subsubcommand() {
pin
dir
uninstall
"###);
");
}

#[test]
Expand Down Expand Up @@ -1014,14 +1004,20 @@ fn help_with_help() {
fn help_with_version() {
let context = TestContext::new_with_versions(&[]);

uv_snapshot!(context.filters(), context.help().arg("--version"), @r###"
success: true
exit_code: 0
uv_snapshot!(context.filters(), context.help().arg("--version"), @r"
success: false
exit_code: 2
----- stdout -----
uv [VERSION] ([COMMIT] DATE)

----- stderr -----
"###);
error: unexpected argument '--version' found

tip: a similar argument exists: '--verbose'

Usage: uv help --verbose... [COMMAND]...

For more information, try '--help'.
");
}

#[test]
Expand Down
26 changes: 12 additions & 14 deletions crates/uv/tests/it/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ fn run_with_python_version() -> Result<()> {
fn run_args() -> Result<()> {
let context = TestContext::new("3.12");

let mut filters = context.filters();
filters.push((r"Usage: (uv|\.exe) run \[OPTIONS\] (?s).*", "[UV RUN HELP]"));
filters.push((r"usage: (\[VENV\]|\[PYTHON-3.12\])(?s).*", "[PYTHON HELP]"));

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#"
[project]
Expand All @@ -158,31 +162,25 @@ fn run_args() -> Result<()> {
})?;

// We treat arguments before the command as uv arguments
uv_snapshot!(context.filters(), context.run().arg("--version").arg("python"), @r###"
uv_snapshot!(filters, context.run().arg("--help").arg("python"), @r"
success: true
exit_code: 0
----- stdout -----
uv [VERSION] ([COMMIT] DATE)
Run a command or script

----- stderr -----
"###);
[UV RUN HELP]
");

// We don't treat arguments after the command as uv arguments
uv_snapshot!(context.filters(), context.run().arg("python").arg("--version"), @r###"
uv_snapshot!(filters, context.run().arg("python").arg("--help"), @r"
success: true
exit_code: 0
----- stdout -----
Python 3.12.[X]

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ foo==1.0.0 (from file://[TEMP_DIR]/)
"###);
[PYTHON HELP]
");

// Can use `--` to separate uv arguments from the command arguments.
uv_snapshot!(context.filters(), context.run().arg("--").arg("python").arg("--version"), @r###"
uv_snapshot!(filters, context.run().arg("--").arg("python").arg("--version"), @r###"
success: true
exit_code: 0
----- stdout -----
Expand Down
43 changes: 20 additions & 23 deletions crates/uv/tests/it/tool_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,43 @@ use uv_static::EnvVars;
#[test]
fn tool_run_args() {
let context = TestContext::new("3.12").with_filtered_counts();
let mut filters = context.filters();
filters.push((
r"Usage: uv tool run \[OPTIONS\] (?s).*",
"[UV TOOL RUN HELP]",
));
filters.push((r"usage: pytest \[options\] (?s).*", "[PYTEST HELP]"));
let tool_dir = context.temp_dir.child("tools");
let bin_dir = context.temp_dir.child("bin");

// We treat arguments before the command as uv arguments
uv_snapshot!(context.filters(), context.tool_run()
.arg("--version")
// We treat arguments before the command as uv tool run arguments
uv_snapshot!(filters, context.tool_run()
.arg("--help")
.arg("pytest")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
success: true
exit_code: 0
----- stdout -----
uv [VERSION] ([COMMIT] DATE)
Run a command provided by a Python package

----- stderr -----
"###);
[UV TOOL RUN HELP]
");

// We don't treat arguments after the command as uv arguments
uv_snapshot!(context.filters(), context.tool_run()
// We don't treat arguments after the command as uv tool run arguments
uv_snapshot!(filters, context.tool_run()
.arg("pytest")
.arg("--version")
.arg("--help")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
success: true
exit_code: 0
----- stdout -----
pytest 8.1.1

----- stderr -----
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
+ iniconfig==2.0.0
+ packaging==24.0
+ pluggy==1.4.0
+ pytest==8.1.1
"###);
[PYTEST HELP]
");

// Can use `--` to separate uv arguments from the command arguments.
uv_snapshot!(context.filters(), context.tool_run()
uv_snapshot!(filters, context.tool_run()
.arg("--")
.arg("pytest")
.arg("--version")
Expand Down
Loading
Loading