From 3de9f35838fcea7d0e52c3f7df3b1ad7032024e2 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 17 Apr 2024 17:40:50 -0500 Subject: [PATCH] Allow `uv run` to execute Python scripts directly e.g. `uv run foo.py` implies `python foo.py` --- crates/uv/src/cli.rs | 3 ++- crates/uv/src/commands/run.rs | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/uv/src/cli.rs b/crates/uv/src/cli.rs index d6c52ef3d6468..393a6f4a68af7 100644 --- a/crates/uv/src/cli.rs +++ b/crates/uv/src/cli.rs @@ -1,3 +1,4 @@ +use std::ffi::OsString; use std::path::PathBuf; use std::str::FromStr; @@ -1632,7 +1633,7 @@ pub(crate) struct RunArgs { /// The arguments to the command. #[arg(allow_hyphen_values = true)] - pub(crate) args: Vec, + pub(crate) args: Vec, /// Always use a new virtual environment for execution. #[arg(long)] diff --git a/crates/uv/src/commands/run.rs b/crates/uv/src/commands/run.rs index c5e88fb740b60..f2e1ea07547ff 100644 --- a/crates/uv/src/commands/run.rs +++ b/crates/uv/src/commands/run.rs @@ -12,6 +12,7 @@ use platform_tags::Tags; use pypi_types::Yanked; use std::ffi::OsString; use std::fmt::Write; +use std::path::PathBuf; use std::{env, iter}; use tempfile::{tempdir_in, TempDir}; use tokio::process::Command; @@ -40,14 +41,28 @@ use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; #[allow(clippy::unnecessary_wraps, clippy::too_many_arguments)] pub(crate) async fn run( target: Option, - args: Vec, + mut args: Vec, mut requirements: Vec, isolated: bool, no_workspace: bool, cache: &Cache, printer: Printer, ) -> Result { - let command = target.unwrap_or("python".to_string()); + let command = if let Some(target) = target { + let target_path = PathBuf::from(&target); + if target_path + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("py")) + && target_path.exists() + { + args.insert(0, target_path.as_os_str().into()); + "python".to_string() + } else { + target + } + } else { + "python".to_string() + }; // Copy the requirements into a set of overrides; we'll use this to prioritize // requested requirements over those discovered in the project. @@ -91,7 +106,10 @@ pub(crate) async fn run( // Standard input, output, and error streams are all inherited // TODO(zanieb): Throw a nicer error message if the command is not found let space = if args.is_empty() { "" } else { " " }; - debug!("Running `{command}{space}{}`", args.join(" ")); + debug!( + "Running `{command}{space}{}`", + args.iter().map(|arg| arg.to_string_lossy()).join(" ") + ); let mut handle = process.spawn()?; let status = handle.wait().await?;