mirror of https://github.com/astral-sh/uv
Restructure interpreter queries
This commit is contained in:
parent
ea4ab29bad
commit
3b343ee9f5
|
|
@ -92,111 +92,78 @@ impl Interpreter {
|
||||||
/// without comparing the patch version number. If that cannot be found, we fall back to
|
/// without comparing the patch version number. If that cannot be found, we fall back to
|
||||||
/// the first available version.
|
/// the first available version.
|
||||||
///
|
///
|
||||||
/// See [`Self::find_strict`] for details on the precedence of Python lookup locations.
|
|
||||||
pub fn find_best(
|
|
||||||
python_version: Option<&PythonVersion>,
|
|
||||||
platform: &Platform,
|
|
||||||
cache: &Cache,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
// First, check for an exact match (or the first available version if no Python version was provided)
|
|
||||||
if let Some(interpreter) = Self::find_version(python_version, platform, cache)? {
|
|
||||||
return Ok(interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(python_version) = python_version {
|
|
||||||
// If that fails, and a specific patch version was requested try again allowing a
|
|
||||||
// different patch version
|
|
||||||
if python_version.patch().is_some() {
|
|
||||||
if let Some(interpreter) =
|
|
||||||
Self::find_version(Some(&python_version.without_patch()), platform, cache)?
|
|
||||||
{
|
|
||||||
return Ok(interpreter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a Python version was requested but cannot be fulfilled, just take any version
|
|
||||||
if let Some(interpreter) = Self::find_version(None, platform, cache)? {
|
|
||||||
return Ok(interpreter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::PythonNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find a Python interpreter.
|
|
||||||
///
|
|
||||||
/// We check, in order, the following locations:
|
/// We check, in order, the following locations:
|
||||||
///
|
///
|
||||||
/// - `VIRTUAL_ENV` and `CONDA_PREFIX`
|
/// - `VIRTUAL_ENV` and `CONDA_PREFIX`
|
||||||
/// - A `.venv` folder
|
/// - A `.venv` folder
|
||||||
/// - 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)
|
||||||
///
|
pub fn find_best(
|
||||||
/// If a version is provided and an interpreter cannot be found with the given version,
|
|
||||||
/// we will return [`None`].
|
|
||||||
pub fn find_version(
|
|
||||||
python_version: Option<&PythonVersion>,
|
python_version: Option<&PythonVersion>,
|
||||||
platform: &Platform,
|
platform: &Platform,
|
||||||
cache: &Cache,
|
cache: &Cache,
|
||||||
) -> Result<Option<Self>, Error> {
|
) -> Result<Self, Error> {
|
||||||
let version_matches = |interpreter: &Self| -> bool {
|
|
||||||
if let Some(python_version) = python_version {
|
|
||||||
// If a patch version was provided, check for an exact match
|
|
||||||
python_version.is_satisfied_by(interpreter)
|
|
||||||
} else {
|
|
||||||
// The version always matches if one was not provided
|
|
||||||
true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let platform = PythonPlatform::from(platform.to_owned());
|
let platform = PythonPlatform::from(platform.to_owned());
|
||||||
if let Some(venv) = detect_virtual_env(&platform)? {
|
|
||||||
let executable = platform.venv_python(venv);
|
|
||||||
let interpreter = Self::query(&executable, &platform.0, cache)?;
|
|
||||||
|
|
||||||
if version_matches(&interpreter) {
|
// Locate the base interpreter, which is our first choice.
|
||||||
return Ok(Some(interpreter));
|
let base_interpreter = Self::find_generic(&platform, cache)?;
|
||||||
|
|
||||||
|
// If a Python version was requested, look for an exact match.
|
||||||
|
if let Some(python_version) = python_version {
|
||||||
|
// If the base interpreter satisfies the requested Python version, we're done.
|
||||||
|
if let Some(interpreter) =
|
||||||
|
base_interpreter.filter(|interpreter| python_version.is_satisfied_by(interpreter))
|
||||||
|
{
|
||||||
|
return Ok(interpreter);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if cfg!(unix) {
|
// If we can find an interpreter with the requested Python version, we're done.
|
||||||
if let Some(python_version) = python_version {
|
if let Some(interpreter) = Self::find_version(python_version, &platform, cache)? {
|
||||||
let requested = format!(
|
return Ok(interpreter);
|
||||||
"python{}.{}",
|
}
|
||||||
python_version.major(),
|
|
||||||
python_version.minor()
|
// If that fails, and a specific patch version was requested try again allowing a
|
||||||
);
|
// different patch version
|
||||||
if let Ok(executable) = which::which(&requested) {
|
if python_version.patch().is_some() {
|
||||||
debug!("Resolved {requested} to {}", executable.display());
|
if let Some(interpreter) =
|
||||||
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
Self::find_version(&python_version.without_patch(), &platform, cache)?
|
||||||
if version_matches(&interpreter) {
|
{
|
||||||
return Ok(Some(interpreter));
|
return Ok(interpreter);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(interpreter) = base_interpreter {
|
||||||
|
return Ok(interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(executable) = which::which("python3") {
|
Err(Error::PythonNotFound)
|
||||||
debug!("Resolved python3 to {}", executable.display());
|
}
|
||||||
|
|
||||||
|
/// Find a Python interpreter that matches the given version.
|
||||||
|
///
|
||||||
|
/// If an interpreter cannot be found with the given version, we will return [`None`].
|
||||||
|
fn find_version(
|
||||||
|
python_version: &PythonVersion,
|
||||||
|
platform: &PythonPlatform,
|
||||||
|
cache: &Cache,
|
||||||
|
) -> Result<Option<Self>, Error> {
|
||||||
|
if cfg!(unix) {
|
||||||
|
let requested = format!(
|
||||||
|
"python{}.{}",
|
||||||
|
python_version.major(),
|
||||||
|
python_version.minor()
|
||||||
|
);
|
||||||
|
if let Ok(executable) = which::which(&requested) {
|
||||||
|
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 python_version.is_satisfied_by(&interpreter) {
|
||||||
return Ok(Some(interpreter));
|
return Ok(Some(interpreter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if cfg!(windows) {
|
} else if cfg!(windows) {
|
||||||
if let Some(python_version) = python_version {
|
if let Some(path) = find_python_windows(python_version.major(), python_version.minor())?
|
||||||
if let Some(path) =
|
{
|
||||||
find_python_windows(python_version.major(), python_version.minor())?
|
let interpreter = Interpreter::query(&path, &platform.0, cache)?;
|
||||||
{
|
if python_version.is_satisfied_by(&interpreter) {
|
||||||
let interpreter = Interpreter::query(&path, &platform.0, cache)?;
|
|
||||||
if version_matches(&interpreter) {
|
|
||||||
return Ok(Some(interpreter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(executable) = which::which("python.exe") {
|
|
||||||
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
|
||||||
if version_matches(&interpreter) {
|
|
||||||
return Ok(Some(interpreter));
|
return Ok(Some(interpreter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -207,6 +174,38 @@ impl Interpreter {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find a generic Python interpreter.
|
||||||
|
///
|
||||||
|
/// We check, in order, the following locations:
|
||||||
|
///
|
||||||
|
/// - `VIRTUAL_ENV` and `CONDA_PREFIX`
|
||||||
|
/// - A `.venv` folder
|
||||||
|
/// - `python3` (unix) or `python.exe` (windows)
|
||||||
|
fn find_generic(platform: &PythonPlatform, cache: &Cache) -> Result<Option<Self>, Error> {
|
||||||
|
if let Some(venv) = detect_virtual_env(platform)? {
|
||||||
|
let executable = platform.venv_python(venv);
|
||||||
|
let interpreter = Self::query(&executable, &platform.0, cache)?;
|
||||||
|
return Ok(Some(interpreter));
|
||||||
|
};
|
||||||
|
|
||||||
|
if cfg!(unix) {
|
||||||
|
if let Ok(executable) = which::which("python3") {
|
||||||
|
debug!("Resolved python3 to {}", executable.display());
|
||||||
|
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
||||||
|
return Ok(Some(interpreter));
|
||||||
|
}
|
||||||
|
} else if cfg!(windows) {
|
||||||
|
if let Ok(executable) = which::which("python.exe") {
|
||||||
|
let interpreter = Interpreter::query(&executable, &platform.0, cache)?;
|
||||||
|
return Ok(Some(interpreter));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unimplemented!("Only Windows and Unix are supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue