mirror of https://github.com/astral-sh/uv
Add environment variable to isolate Python version lookups
This commit is contained in:
parent
b95ae00f3c
commit
930dda1086
|
|
@ -2616,6 +2616,7 @@ dependencies = [
|
||||||
"platform-tags",
|
"platform-tags",
|
||||||
"puffin-cache",
|
"puffin-cache",
|
||||||
"puffin-fs",
|
"puffin-fs",
|
||||||
|
"puffin-warnings",
|
||||||
"regex",
|
"regex",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ platform-host = { path = "../platform-host" }
|
||||||
platform-tags = { path = "../platform-tags" }
|
platform-tags = { path = "../platform-tags" }
|
||||||
puffin-cache = { path = "../puffin-cache" }
|
puffin-cache = { path = "../puffin-cache" }
|
||||||
puffin-fs = { path = "../puffin-fs" }
|
puffin-fs = { path = "../puffin-fs" }
|
||||||
|
puffin-warnings = { path = "../puffin-warnings" }
|
||||||
|
|
||||||
fs-err = { workspace = true, features = ["tokio"] }
|
fs-err = { workspace = true, features = ["tokio"] }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
|
@ -18,6 +19,7 @@ use crate::python_platform::PythonPlatform;
|
||||||
use crate::python_query::find_python_windows;
|
use crate::python_query::find_python_windows;
|
||||||
use crate::virtual_env::detect_virtual_env;
|
use crate::virtual_env::detect_virtual_env;
|
||||||
use crate::{Error, PythonVersion};
|
use crate::{Error, PythonVersion};
|
||||||
|
use puffin_warnings::warn_user_once;
|
||||||
|
|
||||||
/// A Python executable and its associated platform markers.
|
/// A Python executable and its associated platform markers.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -132,6 +134,10 @@ impl Interpreter {
|
||||||
/// - If a python version is given: `pythonx.y`
|
/// - If a python version is given: `pythonx.y`
|
||||||
/// - `python3` (unix) or `python.exe` (windows)
|
/// - `python3` (unix) or `python.exe` (windows)
|
||||||
///
|
///
|
||||||
|
/// If `PUFFIN_PYTHON_PATH` is set, we will not check for Python versions in the
|
||||||
|
/// global PATH, instead we will search using the provided path. Virtual environments
|
||||||
|
/// will still be respected.
|
||||||
|
///
|
||||||
/// If a version is provided and an interpreter cannot be found with the given version,
|
/// If a version is provided and an interpreter cannot be found with the given version,
|
||||||
/// we will return [`None`].
|
/// we will return [`None`].
|
||||||
pub fn find_version(
|
pub fn find_version(
|
||||||
|
|
@ -166,7 +172,8 @@ impl Interpreter {
|
||||||
python_version.major(),
|
python_version.major(),
|
||||||
python_version.minor()
|
python_version.minor()
|
||||||
);
|
);
|
||||||
if let Ok(executable) = which::which(&requested) {
|
|
||||||
|
if let Ok(executable) = Interpreter::find_executable(&requested) {
|
||||||
debug!("Resolved {requested} to {}", executable.display());
|
debug!("Resolved {requested} to {}", executable.display());
|
||||||
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
||||||
if version_matches(&interpreter) {
|
if version_matches(&interpreter) {
|
||||||
|
|
@ -175,7 +182,7 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(executable) = which::which("python3") {
|
if let Ok(executable) = Interpreter::find_executable("python3") {
|
||||||
debug!("Resolved python3 to {}", executable.display());
|
debug!("Resolved python3 to {}", executable.display());
|
||||||
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
||||||
if version_matches(&interpreter) {
|
if version_matches(&interpreter) {
|
||||||
|
|
@ -194,7 +201,7 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(executable) = which::which("python.exe") {
|
if let Ok(executable) = Interpreter::find_executable("python.exe") {
|
||||||
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
||||||
if version_matches(&interpreter) {
|
if version_matches(&interpreter) {
|
||||||
return Ok(Some(interpreter));
|
return Ok(Some(interpreter));
|
||||||
|
|
@ -207,6 +214,26 @@ impl Interpreter {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
|
||||||
|
requested: R,
|
||||||
|
) -> Result<PathBuf, Error> {
|
||||||
|
if let Some(isolated) = std::env::var_os("PUFFIN_PYTHON_PATH") {
|
||||||
|
warn_user_once!(
|
||||||
|
"PUFFIN_PYTHON_PATH is set; ignoring system PATH when looking for binaries."
|
||||||
|
);
|
||||||
|
if let Ok(cwd) = std::env::current_dir() {
|
||||||
|
which::which_in(&requested, Some(isolated), cwd)
|
||||||
|
.map_err(|err| Error::Which(requested.into(), err))
|
||||||
|
} else {
|
||||||
|
which::which_in_global(requested, Some(isolated))
|
||||||
|
.map_err(|err| Error::Which(requested.into(), err))
|
||||||
|
.and_then(|mut paths| paths.next().ok_or(Error::PythonNotFound))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
which::which(&requested).map_err(|err| Error::Which(requested.into(), err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the path to the Python virtual environment.
|
/// Returns the path to the Python virtual environment.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn platform(&self) -> &Platform {
|
pub fn platform(&self) -> &Platform {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTimeError;
|
use std::time::SystemTimeError;
|
||||||
|
|
@ -59,6 +60,6 @@ pub enum Error {
|
||||||
Encode(#[from] rmp_serde::encode::Error),
|
Encode(#[from] rmp_serde::encode::Error),
|
||||||
#[error("Failed to parse pyvenv.cfg")]
|
#[error("Failed to parse pyvenv.cfg")]
|
||||||
Cfg(#[from] cfg::Error),
|
Cfg(#[from] cfg::Error),
|
||||||
#[error("Couldn't find `{0}` in PATH")]
|
#[error("Couldn't find {0:?} in PATH")]
|
||||||
Which(PathBuf, #[source] which::Error),
|
Which(OsString, #[source] which::Error),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::{Error, Interpreter};
|
||||||
|
|
||||||
/// ```text
|
/// ```text
|
||||||
/// -V:3.12 C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe
|
/// -V:3.12 C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe
|
||||||
|
|
@ -34,7 +34,7 @@ pub fn find_requested_python(request: &str) -> Result<PathBuf, Error> {
|
||||||
// `-p 3.10` or `-p 3.10.1`
|
// `-p 3.10` or `-p 3.10.1`
|
||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
let formatted = PathBuf::from(format!("python{request}"));
|
let formatted = PathBuf::from(format!("python{request}"));
|
||||||
which::which_global(&formatted).map_err(|err| Error::Which(formatted, err))
|
Interpreter::find_executable(&formatted)
|
||||||
} else if cfg!(windows) {
|
} else if cfg!(windows) {
|
||||||
if let [major, minor] = versions.as_slice() {
|
if let [major, minor] = versions.as_slice() {
|
||||||
find_python_windows(*major, *minor)?.ok_or(Error::NoSuchPython {
|
find_python_windows(*major, *minor)?.ok_or(Error::NoSuchPython {
|
||||||
|
|
@ -49,8 +49,7 @@ pub fn find_requested_python(request: &str) -> Result<PathBuf, Error> {
|
||||||
}
|
}
|
||||||
} else if !request.contains(std::path::MAIN_SEPARATOR) {
|
} else if !request.contains(std::path::MAIN_SEPARATOR) {
|
||||||
// `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
|
// `-p python3.10`; Generally not used on windows because all Python are `python.exe`.
|
||||||
let request = PathBuf::from(request);
|
Interpreter::find_executable(&request)
|
||||||
which::which_global(&request).map_err(|err| Error::Which(request, err))
|
|
||||||
} else {
|
} else {
|
||||||
// `-p /home/ferris/.local/bin/python3.10`
|
// `-p /home/ferris/.local/bin/python3.10`
|
||||||
Ok(fs_err::canonicalize(request)?)
|
Ok(fs_err::canonicalize(request)?)
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ async fn venv_impl(
|
||||||
find_requested_python(python_request).into_diagnostic()?
|
find_requested_python(python_request).into_diagnostic()?
|
||||||
} else {
|
} else {
|
||||||
fs::canonicalize(
|
fs::canonicalize(
|
||||||
which::which_global("python3")
|
Interpreter::find_executable("python3")
|
||||||
.or_else(|_| which::which_global("python"))
|
.or_else(|_| Interpreter::find_executable("python"))
|
||||||
.map_err(|_| VenvError::PythonNotFound)?,
|
.map_err(|_| VenvError::PythonNotFound)?,
|
||||||
)
|
)
|
||||||
.into_diagnostic()?
|
.into_diagnostic()?
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue