|
2 | 2 |
|
3 | 3 | use std::borrow::Cow;
|
4 | 4 | use std::env;
|
| 5 | +use std::ffi::{OsStr, OsString}; |
5 | 6 | use std::path::PathBuf;
|
6 | 7 |
|
7 | 8 | use tracing::{debug, instrument};
|
@@ -58,7 +59,7 @@ pub fn find_requested_python(
|
58 | 59 | }
|
59 | 60 | } else if !request.contains(std::path::MAIN_SEPARATOR) {
|
60 | 61 | // `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
|
61 |
| - let Some(executable) = Interpreter::find_executable(request)? else { |
| 62 | + let Some(executable) = find_executable(request)? else { |
62 | 63 | return Ok(None);
|
63 | 64 | };
|
64 | 65 | Interpreter::query(&executable, platform.clone(), cache).map(Some)
|
@@ -198,6 +199,41 @@ fn find_python(
|
198 | 199 | Ok(None)
|
199 | 200 | }
|
200 | 201 |
|
| 202 | +/// Find the Python interpreter in `PATH` matching the given name (e.g., `python3`, respecting |
| 203 | +/// `UV_PYTHON_PATH`. |
| 204 | +/// |
| 205 | +/// Returns `Ok(None)` if not found. |
| 206 | +fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>( |
| 207 | + requested: R, |
| 208 | +) -> Result<Option<PathBuf>, Error> { |
| 209 | + #[allow(non_snake_case)] |
| 210 | + let UV_TEST_PYTHON_PATH = env::var_os("UV_TEST_PYTHON_PATH"); |
| 211 | + |
| 212 | + #[allow(non_snake_case)] |
| 213 | + let PATH = UV_TEST_PYTHON_PATH |
| 214 | + .or(env::var_os("PATH")) |
| 215 | + .unwrap_or_default(); |
| 216 | + |
| 217 | + // We use `which` here instead of joining the paths ourselves because `which` checks for us if the python |
| 218 | + // binary is executable and exists. It also has some extra logic that handles inconsistent casing on Windows |
| 219 | + // and expands `~`. |
| 220 | + for path in env::split_paths(&PATH) { |
| 221 | + let paths = match which::which_in_global(requested, Some(&path)) { |
| 222 | + Ok(paths) => paths, |
| 223 | + Err(which::Error::CannotFindBinaryPath) => continue, |
| 224 | + Err(err) => return Err(Error::WhichError(requested.into(), err)), |
| 225 | + }; |
| 226 | + for path in paths { |
| 227 | + if cfg!(windows) && windows::is_windows_store_shim(&path) { |
| 228 | + continue; |
| 229 | + } |
| 230 | + return Ok(Some(path)); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + Ok(None) |
| 235 | +} |
| 236 | + |
201 | 237 | #[derive(Debug, Clone)]
|
202 | 238 | enum PythonInstallation {
|
203 | 239 | PyListPath {
|
|
0 commit comments