From c22e15f07d0229fc520e83c7ae98bbcc6756d879 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 24 Apr 2024 12:51:57 -0500 Subject: [PATCH] Warn when an unsupported Python version is encountered (#3250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I rebased https://github.com/astral-sh/uv/pull/2757 then realized that we want to implement this for more than `uv venv`. Closes https://github.com/astral-sh/uv/issues/2587 Closes https://github.com/astral-sh/uv/issues/2757 ``` ❯ cargo run -q -- pip install -p /Users/mz/bin/python3.7 anyio warning: uv is only compatible with Python 3.8+, found Python 3.7.17. Audited 1 package in 84ms ❯ cargo run -q -- venv -p /Users/mz/bin/python3.7 warning: uv is only compatible with Python 3.8+, found Python 3.7.17. Using Python 3.7.17 interpreter at: /Users/mz/bin/python3.7 Creating virtualenv at: .venv Activate with: source .venv/bin/activate ``` --------- Co-authored-by: Stevie Gayet --- Cargo.lock | 1 + crates/uv-interpreter/Cargo.toml | 1 + crates/uv-interpreter/src/find_python.rs | 43 ++++++++++++++++++------ crates/uv/tests/pip_compile.rs | 1 + 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d576f94b..1a28d5976 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4862,6 +4862,7 @@ dependencies = [ "uv-cache", "uv-fs", "uv-toolchain", + "uv-warnings", "which", "winapi", ] diff --git a/crates/uv-interpreter/Cargo.toml b/crates/uv-interpreter/Cargo.toml index bdfe6f338..4076b2a39 100644 --- a/crates/uv-interpreter/Cargo.toml +++ b/crates/uv-interpreter/Cargo.toml @@ -22,6 +22,7 @@ pypi-types = { workspace = true } uv-cache = { workspace = true } uv-fs = { workspace = true } uv-toolchain = { workspace = true } +uv-warnings = { workspace = true } configparser = { workspace = true } fs-err = { workspace = true, features = ["tokio"] } diff --git a/crates/uv-interpreter/src/find_python.rs b/crates/uv-interpreter/src/find_python.rs index 6d119a262..c00d9a166 100644 --- a/crates/uv-interpreter/src/find_python.rs +++ b/crates/uv-interpreter/src/find_python.rs @@ -7,6 +7,7 @@ use tracing::{debug, instrument}; use uv_cache::Cache; use uv_toolchain::PythonVersion; +use uv_warnings::warn_user_once; use crate::interpreter::InterpreterInfoError; use crate::python_environment::{detect_python_executable, detect_virtual_env}; @@ -42,7 +43,11 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result unreachable!(), }; - find_python(selector, cache) + let interpreter = find_python(selector, cache)?; + interpreter + .as_ref() + .inspect(|inner| warn_on_unsupported_python(inner)); + Ok(interpreter) } else { match fs_err::metadata(request) { Ok(metadata) => { @@ -61,14 +66,18 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result { // `-p python3.10`; Generally not used on windows because all Python are `python.exe`. let Some(executable) = find_executable(request)? else { return Ok(None); }; - Interpreter::query(executable, cache).map(Some) + Interpreter::query(executable, cache) + .inspect(warn_on_unsupported_python) + .map(Some) } Err(err) => return Err(err.into()), } @@ -82,13 +91,15 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result Result { debug!("Starting interpreter discovery for default Python"); - try_find_default_python(cache)?.ok_or(if cfg!(windows) { - Error::NoPythonInstalledWindows - } else if cfg!(unix) { - Error::NoPythonInstalledUnix - } else { - unreachable!("Only Unix and Windows are supported") - }) + try_find_default_python(cache)? + .ok_or(if cfg!(windows) { + Error::NoPythonInstalledWindows + } else if cfg!(unix) { + Error::NoPythonInstalledUnix + } else { + unreachable!("Only Unix and Windows are supported") + }) + .inspect(warn_on_unsupported_python) } /// Same as [`find_default_python`] but returns `None` if no python is found instead of returning an `Err`. @@ -412,6 +423,16 @@ impl PythonVersionSelector { } } +fn warn_on_unsupported_python(interpreter: &Interpreter) { + // Warn on usage with an unsupported Python version + if interpreter.python_tuple() < (3, 8) { + warn_user_once!( + "uv is only compatible with Python 3.8+, found Python {}.", + interpreter.python_version() + ); + } +} + /// Find a matching Python or any fallback Python. /// /// If no Python version is provided, we will use the first available interpreter. @@ -439,6 +460,7 @@ pub fn find_best_python( // First, check for an exact match (or the first available version if no Python version was provided) if let Some(interpreter) = find_version(python_version, system, cache)? { + warn_on_unsupported_python(&interpreter); return Ok(interpreter); } @@ -449,6 +471,7 @@ pub fn find_best_python( if let Some(interpreter) = find_version(Some(&python_version.without_patch()), system, cache)? { + warn_on_unsupported_python(&interpreter); return Ok(interpreter); } } diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 31e1977f1..80b811ff9 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -913,6 +913,7 @@ fn compile_python_37() -> Result<()> { "warning: The requested Python version 3.7 is not available; .* will be used to build dependencies instead.\n", "", ), + (r"warning: uv is only compatible with Python 3\.8\+, found Python 3\.7.*\n", "") ] .into_iter() .chain(context.filters())