mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 12:59:45 +00:00
Ignore Python 2 installations when querying for interpreters (#1905)
## Summary Fixes https://github.com/astral-sh/uv/issues/1693 `uv` currently fails when a user has `python` 2 or older installed on their system without a `python3` or `python3.exe` on their path because the `get_interpreter_info.py` script fails executing (it uses some Python 3+ APIs). This PR fixes this by: * Returning an explicit error code in `get_interpreter_info` if the Python version isn't supported * Skipping over this error in `python_query` if the user requested ANY python version or a version >= 3. * Error if the user requested a Python 2 version. ## Test Plan Error if the user requests a legacy python version. ``` uv venv -p 2 × Python 2 or older is not supported. Please use Python 3 or newer. ``` Ignore any python 2 installation when querying newer python installations (using v4 here because I have python3 on the path and that takes precedence over querying python) ``` uv_interpreter::python_query::find_python selector=Major(4) 0.005541s 0ms DEBUG uv_interpreter::interpreter Detecting markers for: /home/micha/.pyenv/shims/python 0.059730s 54ms DEBUG uv_interpreter::python_query Found a Python 2 installation that isn't supported by uv, skipping. 0.059983s 54ms DEBUG uv_interpreter::interpreter Using cached markers for: /usr/bin/python × No Python 4 In `PATH`. Is Python 4 installed? ```
This commit is contained in:
parent
73ed0f0cf1
commit
829e14769d
4 changed files with 49 additions and 8 deletions
|
|
@ -1,3 +1,13 @@
|
|||
""""
|
||||
Queries information about the current Python interpreter and prints it as JSON.
|
||||
|
||||
Exit Codes:
|
||||
0: Success
|
||||
1: General failure
|
||||
3: Python version 3 or newer is required
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
|
|
@ -13,6 +23,10 @@ def format_full_version(info):
|
|||
return version
|
||||
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
if hasattr(sys, "implementation"):
|
||||
implementation_version = format_full_version(sys.implementation.version)
|
||||
implementation_name = sys.implementation.name
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub struct Interpreter {
|
|||
impl Interpreter {
|
||||
/// Detect the interpreter info for the given Python executable.
|
||||
pub fn query(executable: &Path, platform: &Platform, cache: &Cache) -> Result<Self, Error> {
|
||||
let info = InterpreterQueryResult::query_cached(executable, cache)?;
|
||||
let info = InterpreterInfo::query_cached(executable, cache)?;
|
||||
|
||||
debug_assert!(
|
||||
info.sys_executable.is_absolute(),
|
||||
|
|
@ -290,7 +290,7 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub(crate) struct InterpreterQueryResult {
|
||||
pub(crate) struct InterpreterInfo {
|
||||
pub(crate) markers: MarkerEnvironment,
|
||||
pub(crate) base_exec_prefix: PathBuf,
|
||||
pub(crate) base_prefix: PathBuf,
|
||||
|
|
@ -298,8 +298,8 @@ pub(crate) struct InterpreterQueryResult {
|
|||
pub(crate) sys_executable: PathBuf,
|
||||
}
|
||||
|
||||
impl InterpreterQueryResult {
|
||||
/// Return the resolved [`InterpreterQueryResult`] for the given Python executable.
|
||||
impl InterpreterInfo {
|
||||
/// Return the resolved [`InterpreterInfo`] for the given Python executable.
|
||||
pub(crate) fn query(interpreter: &Path) -> Result<Self, Error> {
|
||||
let script = include_str!("get_interpreter_info.py");
|
||||
let output = if cfg!(windows)
|
||||
|
|
@ -349,6 +349,10 @@ impl InterpreterQueryResult {
|
|||
// stderr isn't technically a criterion for success, but i don't know of any cases where there
|
||||
// should be stderr output and if there is, we want to know
|
||||
if !output.status.success() || !output.stderr.is_empty() {
|
||||
if output.status.code() == Some(3) {
|
||||
return Err(Error::Python2OrOlder);
|
||||
}
|
||||
|
||||
return Err(Error::PythonSubcommandOutput {
|
||||
message: format!(
|
||||
"Querying Python at `{}` failed with status {}",
|
||||
|
|
@ -359,7 +363,8 @@ impl InterpreterQueryResult {
|
|||
stderr: String::from_utf8_lossy(&output.stderr).trim().to_string(),
|
||||
});
|
||||
}
|
||||
let data = serde_json::from_slice::<Self>(&output.stdout).map_err(|err| {
|
||||
|
||||
let data: Self = serde_json::from_slice(&output.stdout).map_err(|err| {
|
||||
Error::PythonSubcommandOutput {
|
||||
message: format!(
|
||||
"Querying Python at `{}` did not return the expected data: {err}",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ pub enum Error {
|
|||
stdout: String,
|
||||
stderr: String,
|
||||
},
|
||||
#[error("Python 2 or older is not supported. Please use Python 3 or newer.")]
|
||||
Python2OrOlder,
|
||||
#[error("Failed to write to cache")]
|
||||
Encode(#[from] rmp_serde::encode::Error),
|
||||
#[error("Broken virtualenv: Failed to parse pyvenv.cfg")]
|
||||
|
|
|
|||
|
|
@ -146,9 +146,20 @@ fn find_python(
|
|||
continue;
|
||||
}
|
||||
|
||||
let installation = PythonInstallation::Interpreter(Interpreter::query(
|
||||
&path, platform, cache,
|
||||
)?);
|
||||
let interpreter = match Interpreter::query(&path, platform, cache) {
|
||||
Ok(interpreter) => interpreter,
|
||||
Err(Error::Python2OrOlder) => {
|
||||
if selector.major() <= Some(2) {
|
||||
return Err(Error::Python2OrOlder);
|
||||
}
|
||||
// Skip over Python 2 or older installation when querying for a recent python installation.
|
||||
tracing::debug!("Found a Python 2 installation that isn't supported by uv, skipping.");
|
||||
continue;
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
|
||||
let installation = PythonInstallation::Interpreter(interpreter);
|
||||
|
||||
if let Some(interpreter) = installation.select(selector, platform, cache)? {
|
||||
return Ok(Some(interpreter));
|
||||
|
|
@ -306,6 +317,15 @@ impl PythonVersionSelector {
|
|||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn major(self) -> Option<u8> {
|
||||
match self {
|
||||
PythonVersionSelector::Default => None,
|
||||
PythonVersionSelector::Major(major) => Some(major),
|
||||
PythonVersionSelector::MajorMinor(major, _) => Some(major),
|
||||
PythonVersionSelector::MajorMinorPatch(major, _, _) => Some(major),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod windows {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue