From d1549af5a869771d0493ed060494a48f26ce6191 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 5 Mar 2024 17:50:06 -0500 Subject: [PATCH] Escape Windows paths with spaces in venv activation command --- crates/uv/src/commands/venv.rs | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index e37b7568b1d8..ca3b5e361549 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -216,29 +216,21 @@ async fn venv_impl( None => None, Some(Shell::Bash | Shell::Zsh) => Some(format!( "source {}", - shlex(path.join("bin").join("activate")) + shlex_posix(path.join("bin").join("activate")) )), Some(Shell::Fish) => Some(format!( "source {}", - shlex(path.join("bin").join("activate.fish")) + shlex_posix(path.join("bin").join("activate.fish")) )), Some(Shell::Nushell) => Some(format!( "overlay use {}", - shlex(path.join("bin").join("activate.nu")) + shlex_posix(path.join("bin").join("activate.nu")) )), Some(Shell::Csh) => Some(format!( "source {}", - shlex(path.join("bin").join("activate.csh")) + shlex_posix(path.join("bin").join("activate.csh")) )), - Some(Shell::Powershell) => { - // No need to quote the path for PowerShell. - Some( - path.join("Scripts") - .join("activate") - .simplified_display() - .to_string(), - ) - } + Some(Shell::Powershell) => Some(shlex_windows(path.join("Scripts").join("activate"))), }; if let Some(act) = activation { writeln!(printer, "Activate with: {}", act.green()).into_diagnostic()?; @@ -247,8 +239,8 @@ async fn venv_impl( Ok(ExitStatus::Success) } -/// Quote a path, if necessary, for safe use in a shell command. -fn shlex(executable: impl AsRef) -> String { +/// Quote a path, if necessary, for safe use in a POSIX-compatible shell command. +fn shlex_posix(executable: impl AsRef) -> String { // Convert to a display path. let executable = executable.as_ref().simplified_display().to_string(); @@ -261,3 +253,17 @@ fn shlex(executable: impl AsRef) -> String { executable } } + +/// Quote a path, if necessary, for safe use in `PowerShell`. +fn shlex_windows(executable: impl AsRef) -> String { + // Convert to a display path. + let executable = executable.as_ref().simplified_display().to_string(); + + // Wrap the executable in quotes (and a `&` invocation) if it contains spaces. + // TODO(charlie): This won't work in `cmd.exe`. + if executable.contains(' ') { + format!("& \"{executable}\"") + } else { + executable + } +}