mirror of
https://github.com/astral-sh/uv
synced 2026-01-24 06:50:18 -05:00
feat: cmd.exe detection heuristic (#2226)
## Summary Follow up from discussion in https://github.com/astral-sh/uv/pull/2223 Detect CMD.exe by checking if `PROMPT` env var is set on windows, otherwise assume it's PowerShell. Note, this will not work if user modifies their system env vars to include `PROMPT` by default or if they launch nested PowerShell from Command Prompt (e.g. `Developer PowerShell for VS 2022`). ## Test Plan Only tested locally, although we try to add some CI tests that specifically use CMD.exe Command Prompt ``` Microsoft Windows [Version 10.0.19044.3086] (c) Microsoft Corporation. All rights reserved. Z:\Users\samypr100\dev\uv>Z:\Users\samypr100\.cargo\bin\cargo.exe +stable run --color=always -- venv "Foo Bar" Finished dev [unoptimized + debuginfo] target(s) in 0.69s Running `target\debug\uv.exe venv "Foo Bar"` Using Python 3.12.2 interpreter at: Z:\Users\samypr100\AppData\Local\Programs\Python\Python312\python.exe Creating virtualenv at: Foo Bar Activate with: "Foo Bar\Scripts\activate" ``` Power Shell ``` Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Try the new cross-platform PowerShell https://aka.ms/pscore6 PS Z:\Users\samypr100\dev\uv>Z:\Users\samypr100\.cargo\bin\cargo.exe +stable run --color=always -- venv "Foo Bar" Finished dev [unoptimized + debuginfo] target(s) in 0.63s Running `target\debug\uv.exe venv "Foo Bar"` Using Python 3.12.2 interpreter at: Z:\Users\samypr100\AppData\Local\Programs\Python\Python312\python.exe Creating virtualenv at: Foo Bar Activate with: & "Foo Bar\Scripts\activate" ```
This commit is contained in:
@@ -230,7 +230,14 @@ async fn venv_impl(
|
||||
"source {}",
|
||||
shlex_posix(path.join("bin").join("activate.csh"))
|
||||
)),
|
||||
Some(Shell::Powershell) => Some(shlex_windows(path.join("Scripts").join("activate"))),
|
||||
Some(Shell::Powershell) => Some(shlex_windows(
|
||||
path.join("Scripts").join("activate"),
|
||||
Shell::Powershell,
|
||||
)),
|
||||
Some(Shell::Cmd) => Some(shlex_windows(
|
||||
path.join("Scripts").join("activate"),
|
||||
Shell::Cmd,
|
||||
)),
|
||||
};
|
||||
if let Some(act) = activation {
|
||||
writeln!(printer.stderr(), "Activate with: {}", act.green()).into_diagnostic()?;
|
||||
@@ -254,15 +261,20 @@ fn shlex_posix(executable: impl AsRef<Path>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Quote a path, if necessary, for safe use in `PowerShell`.
|
||||
fn shlex_windows(executable: impl AsRef<Path>) -> String {
|
||||
/// Quote a path, if necessary, for safe use in `PowerShell` and `cmd`.
|
||||
fn shlex_windows(executable: impl AsRef<Path>, shell: Shell) -> 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`.
|
||||
// Wrap the executable in quotes (and a `&` invocation on PowerShell), if it contains spaces.
|
||||
if executable.contains(' ') {
|
||||
format!("& \"{executable}\"")
|
||||
if shell == Shell::Powershell {
|
||||
// For PowerShell, wrap in a `&` invocation.
|
||||
format!("& \"{executable}\"")
|
||||
} else {
|
||||
// Otherwise, assume `cmd`, which doesn't need the `&`.
|
||||
format!("\"{executable}\"")
|
||||
}
|
||||
} else {
|
||||
executable
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ pub(crate) enum Shell {
|
||||
Fish,
|
||||
/// PowerShell
|
||||
Powershell,
|
||||
/// Cmd (Command Prompt)
|
||||
Cmd,
|
||||
/// Z SHell (zsh)
|
||||
Zsh,
|
||||
/// Nushell
|
||||
@@ -34,7 +36,14 @@ impl Shell {
|
||||
} else if let Some(env_shell) = std::env::var_os("SHELL") {
|
||||
Shell::from_shell_path(env_shell)
|
||||
} else if cfg!(windows) {
|
||||
Some(Shell::Powershell)
|
||||
// Command Prompt relies on PROMPT for its appearance whereas PowerShell does not.
|
||||
// See: https://stackoverflow.com/a/66415037.
|
||||
if std::env::var_os("PROMPT").is_some() {
|
||||
Some(Shell::Cmd)
|
||||
} else {
|
||||
// Fallback to PowerShell if the PROMPT environment variable is not set.
|
||||
Some(Shell::Powershell)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user