Warn when an unsupported Python version is encountered (#3250)

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 <stegayet@users.noreply.github.com>
This commit is contained in:
Zanie Blue 2024-04-24 12:51:57 -05:00 committed by GitHub
parent 20e9589662
commit c22e15f07d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 36 additions and 10 deletions

1
Cargo.lock generated
View file

@ -4862,6 +4862,7 @@ dependencies = [
"uv-cache", "uv-cache",
"uv-fs", "uv-fs",
"uv-toolchain", "uv-toolchain",
"uv-warnings",
"which", "which",
"winapi", "winapi",
] ]

View file

@ -22,6 +22,7 @@ pypi-types = { workspace = true }
uv-cache = { workspace = true } uv-cache = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-toolchain = { workspace = true } uv-toolchain = { workspace = true }
uv-warnings = { workspace = true }
configparser = { workspace = true } configparser = { workspace = true }
fs-err = { workspace = true, features = ["tokio"] } fs-err = { workspace = true, features = ["tokio"] }

View file

@ -7,6 +7,7 @@ use tracing::{debug, instrument};
use uv_cache::Cache; use uv_cache::Cache;
use uv_toolchain::PythonVersion; use uv_toolchain::PythonVersion;
use uv_warnings::warn_user_once;
use crate::interpreter::InterpreterInfoError; use crate::interpreter::InterpreterInfoError;
use crate::python_environment::{detect_python_executable, detect_virtual_env}; use crate::python_environment::{detect_python_executable, detect_virtual_env};
@ -42,7 +43,11 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result<Option<Inte
// SAFETY: Guaranteed by the Ok(versions) guard // SAFETY: Guaranteed by the Ok(versions) guard
_ => unreachable!(), _ => unreachable!(),
}; };
find_python(selector, cache) let interpreter = find_python(selector, cache)?;
interpreter
.as_ref()
.inspect(|inner| warn_on_unsupported_python(inner));
Ok(interpreter)
} else { } else {
match fs_err::metadata(request) { match fs_err::metadata(request) {
Ok(metadata) => { Ok(metadata) => {
@ -61,14 +66,18 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result<Option<Inte
// `-p /home/ferris/.local/bin/python3.10` // `-p /home/ferris/.local/bin/python3.10`
path path
}; };
Interpreter::query(executable, cache).map(Some) Interpreter::query(executable, cache)
.inspect(warn_on_unsupported_python)
.map(Some)
} }
Err(err) if err.kind() == std::io::ErrorKind::NotFound => { Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
// `-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 Some(executable) = find_executable(request)? else { let Some(executable) = find_executable(request)? else {
return Ok(None); 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()), Err(err) => return Err(err.into()),
} }
@ -82,13 +91,15 @@ pub fn find_requested_python(request: &str, cache: &Cache) -> Result<Option<Inte
#[instrument(skip_all)] #[instrument(skip_all)]
pub fn find_default_python(cache: &Cache) -> Result<Interpreter, Error> { pub fn find_default_python(cache: &Cache) -> Result<Interpreter, Error> {
debug!("Starting interpreter discovery for default Python"); debug!("Starting interpreter discovery for default Python");
try_find_default_python(cache)?.ok_or(if cfg!(windows) { try_find_default_python(cache)?
Error::NoPythonInstalledWindows .ok_or(if cfg!(windows) {
} else if cfg!(unix) { Error::NoPythonInstalledWindows
Error::NoPythonInstalledUnix } else if cfg!(unix) {
} else { Error::NoPythonInstalledUnix
unreachable!("Only Unix and Windows are supported") } 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`. /// 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. /// Find a matching Python or any fallback Python.
/// ///
/// If no Python version is provided, we will use the first available interpreter. /// 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) // 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)? { if let Some(interpreter) = find_version(python_version, system, cache)? {
warn_on_unsupported_python(&interpreter);
return Ok(interpreter); return Ok(interpreter);
} }
@ -449,6 +471,7 @@ pub fn find_best_python(
if let Some(interpreter) = if let Some(interpreter) =
find_version(Some(&python_version.without_patch()), system, cache)? find_version(Some(&python_version.without_patch()), system, cache)?
{ {
warn_on_unsupported_python(&interpreter);
return Ok(interpreter); return Ok(interpreter);
} }
} }

View file

@ -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", "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() .into_iter()
.chain(context.filters()) .chain(context.filters())