Skip to content

Commit

Permalink
Avoid Windows Store shims in --python python3-like invocations (#2212)
Browse files Browse the repository at this point in the history
## Summary

We have logic in `python_query.rs` to filter out Windows Store shims
when you use invocations like `-p 3.10`, but not `--python python3`,
which is uncommon but allowed on Windows.

Closes #2211.
  • Loading branch information
charliermarsh authored Mar 5, 2024
1 parent cf94df7 commit 01ebaef
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 21 deletions.
20 changes: 0 additions & 20 deletions crates/uv-interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::ffi::{OsStr, OsString};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
Expand Down Expand Up @@ -200,25 +199,6 @@ impl Interpreter {
}
}

/// Find the Python interpreter in `PATH`, respecting `UV_PYTHON_PATH`.
///
/// Returns `Ok(None)` if not found.
pub(crate) fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
requested: R,
) -> Result<Option<PathBuf>, Error> {
let result = if let Some(isolated) = std::env::var_os("UV_TEST_PYTHON_PATH") {
which::which_in(requested, Some(isolated), std::env::current_dir()?)
} else {
which::which(requested)
};

match result {
Err(which::Error::CannotFindBinaryPath) => Ok(None),
Err(err) => Err(Error::WhichError(requested.into(), err)),
Ok(path) => Ok(Some(path)),
}
}

/// Returns the path to the Python virtual environment.
#[inline]
pub fn platform(&self) -> &Platform {
Expand Down
38 changes: 37 additions & 1 deletion crates/uv-interpreter/src/python_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

use tracing::{debug, instrument};
Expand Down Expand Up @@ -58,7 +59,7 @@ pub fn find_requested_python(
}
} else if !request.contains(std::path::MAIN_SEPARATOR) {
// `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
let Some(executable) = Interpreter::find_executable(request)? else {
let Some(executable) = find_executable(request)? else {
return Ok(None);
};
Interpreter::query(&executable, platform.clone(), cache).map(Some)
Expand Down Expand Up @@ -198,6 +199,41 @@ fn find_python(
Ok(None)
}

/// Find the Python interpreter in `PATH` matching the given name (e.g., `python3`, respecting
/// `UV_PYTHON_PATH`.
///
/// Returns `Ok(None)` if not found.
fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
requested: R,
) -> Result<Option<PathBuf>, Error> {
#[allow(non_snake_case)]
let UV_TEST_PYTHON_PATH = env::var_os("UV_TEST_PYTHON_PATH");

#[allow(non_snake_case)]
let PATH = UV_TEST_PYTHON_PATH
.or(env::var_os("PATH"))
.unwrap_or_default();

// We use `which` here instead of joining the paths ourselves because `which` checks for us if the python
// binary is executable and exists. It also has some extra logic that handles inconsistent casing on Windows
// and expands `~`.
for path in env::split_paths(&PATH) {
let paths = match which::which_in_global(requested, Some(&path)) {
Ok(paths) => paths,
Err(which::Error::CannotFindBinaryPath) => continue,
Err(err) => return Err(Error::WhichError(requested.into(), err)),
};
for path in paths {
if cfg!(windows) && windows::is_windows_store_shim(&path) {
continue;
}
return Ok(Some(path));
}
}

Ok(None)
}

#[derive(Debug, Clone)]
enum PythonInstallation {
PyListPath {
Expand Down

0 comments on commit 01ebaef

Please sign in to comment.