From 885ac9d71411c006f9d7e896abe4efb2f5c7530e Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 23 Oct 2025 16:32:29 +0200 Subject: [PATCH 1/3] Don't duplicate exit status Previously: ``` error: Querying Python at `/home/konsti/projects/pdm/.venv/bin/python3` failed with exit status exit status: 1 ``` --- crates/uv-python/src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 644215f100394..691d253ba7366 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -779,7 +779,7 @@ impl Display for StatusCodeError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "Querying Python at `{}` failed with exit status {}", + "Querying Python at `{}` failed with {}", self.path.display(), self.code )?; From 1bee45b38f7616bc80f89c733283ad38df4965c2 Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 23 Oct 2025 17:39:16 +0200 Subject: [PATCH 2/3] Show a better hint for broken PBS venvs For some reason, I'm encountering broken PBS venvs again (https://github.com/astral-sh/python-build-standalone/issues/380). Instead of showing the obscure error to the user, we point them to the PBS bug and ask them to run `uv venv`. ``` $ uv sync error: Querying Python at `/tmp/uv/tests/.tmpwXZRTI/temp/.venv/bin/python3` failed with exit status exit status: 1 [stderr] Could not find platform independent libraries Could not find platform dependent libraries Python path configuration: PYTHONHOME = (not set) PYTHONPATH = (not set) program name = '/tmp/uv/tests/.tmpwXZRTI/temp/.venv/bin/python3' isolated = 1 environment = 0 user site = 0 safe_path = 1 import site = 1 is in build tree = 0 stdlib dir = '/install/lib/python3.12' sys._base_executable = '/home/konsti/.local/share/uv/python/cpython-3.12.9-linux-x86_64-gnu/bin/python3.12' sys.base_prefix = '/install' sys.base_exec_prefix = '/install' sys.platlibdir = 'lib' sys.executable = '/tmp/uv/tests/.tmpwXZRTI/temp/.venv/bin/python3' sys.prefix = '/install' sys.exec_prefix = '/install' sys.path = [ '/install/lib/python312.zip', '/install/lib/python3.12', '/install/lib/python3.12/lib-dynload', ] Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding Python runtime state: core initialized ModuleNotFoundError: No module named 'encodings' Current thread 0x000078e48b835740 (most recent call first): ``` ``` $ uv-debug sync error: Can't use Python at `/tmp/uv/tests/.tmpwXZRTI/temp/.venv/bin/python3` Caused by: Python is missing PYTHONHOME. If you are using a managed Python interpreter, this is a known bug (https://github.com/astral-sh/python-build-standalone/issues/380). You can recreate the virtual environment with `uv venv`. ``` --- crates/uv-python/src/interpreter.rs | 49 +++++++++++++++++++--------- crates/uv/tests/it/python_install.rs | 46 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 691d253ba7366..52776668dd810 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -903,6 +903,27 @@ pub enum InterpreterInfoError { }, #[error("Only Pyodide is support for Emscripten Python")] EmscriptenNotPyodide, + #[error("Python is missing PYTHONHOME. If you are using a managed Python interpreter, this is a known bug (https://github.com/astral-sh/python-build-standalone/issues/380). You can recreate the virtual environment with `{}`.", "uv venv".green())] + PythonHomeNotFound, +} + +impl InterpreterInfoError { + /// Check whether the stderr of `python` matches a known pattern. + pub(crate) fn from_query_stderr(stderr: &str) -> Option { + // If the Python version is too old, we may not even be able to invoke the query script + if stderr.contains("Unknown option: -I") { + return Some(Self::UnsupportedPython); + } + + // Until we fixed the PBS bug, inform the user that this is bug on our side and can be fixed + // with `uv venv`. + // https://github.com/astral-sh/python-build-standalone/issues/380 + if stderr.contains("ModuleNotFoundError: No module named 'encodings'") { + return Some(Self::PythonHomeNotFound); + } + + None + } } #[allow(clippy::struct_excessive_bools)] @@ -979,10 +1000,9 @@ impl InterpreterInfo { if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); - // If the Python version is too old, we may not even be able to invoke the query script - if stderr.contains("Unknown option: -I") { + if let Some(query_error) = InterpreterInfoError::from_query_stderr(&stderr) { return Err(Error::QueryScript { - err: InterpreterInfoError::UnsupportedPython, + err: query_error, path: interpreter.to_path_buf(), }); } @@ -999,20 +1019,19 @@ impl InterpreterInfo { serde_json::from_slice(&output.stdout).map_err(|err| { let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); - // If the Python version is too old, we may not even be able to invoke the query script - if stderr.contains("Unknown option: -I") { - Error::QueryScript { - err: InterpreterInfoError::UnsupportedPython, - path: interpreter.to_path_buf(), - } - } else { - Error::UnexpectedResponse(UnexpectedResponseError { - err, - stdout: String::from_utf8_lossy(&output.stdout).trim().to_string(), - stderr, + if let Some(query_error) = InterpreterInfoError::from_query_stderr(&stderr) { + return Error::QueryScript { + err: query_error, path: interpreter.to_path_buf(), - }) + }; } + + Error::UnexpectedResponse(UnexpectedResponseError { + err, + stdout: String::from_utf8_lossy(&output.stdout).trim().to_string(), + stderr, + path: interpreter.to_path_buf(), + }) })?; match result { diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index ada7c727a9017..781e9d51253b6 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use std::{env, path::Path, process::Command}; use crate::common::{TestContext, uv_snapshot}; +use anyhow::Result; use assert_cmd::assert::OutputAssertExt; use assert_fs::{ assert::PathAssert, @@ -3650,3 +3651,48 @@ fn python_install_build_version_pypy() { error: No download found for request: pypy-3.10-[PLATFORM] "); } + +/// Show a fitting error message for +/// . +#[cfg(unix)] +#[test] +fn missing_python_home_error_message() -> Result<()> { + let context = TestContext::new("3.12"); + + // Create a Python project so we can use `uv sync` + context.init().assert().success(); + + // Create a broken venv from a symlink. + let sys_executable = fs_err::canonicalize(context.venv.join("bin").join("python"))?; + fs_err::os::unix::fs::symlink(sys_executable, context.temp_dir.join("python-link"))?; + fs_err::remove_dir_all(context.venv.as_ref())?; + Command::new(context.temp_dir.join("python-link")) + .arg("-m") + .arg("venv") + .arg("--without-pip") + .arg(context.venv.as_ref()) + .assert() + .success(); + + uv_snapshot!(context.filters(), context.pip_list().arg("-p").arg(context.venv.join("bin").join("python")), @r" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to inspect Python interpreter from provided path at `.venv/bin/python` + Caused by: Can't use Python at `[VENV]/bin/python` + Caused by: Python is missing PYTHONHOME. If you are using a managed Python interpreter, this is a known bug (https://github.com/astral-sh/python-build-standalone/issues/380). You can recreate the virtual environment with `uv venv`. + "); + + // By default, we skip broken interpreters + uv_snapshot!(context.filters(), context.pip_list(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "); + + Ok(()) +} From cc905306b4530f2f7ea9c8381031caa103b45b05 Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 23 Oct 2025 17:51:12 +0200 Subject: [PATCH 3/3] Windows clippy --- crates/uv/tests/it/python_install.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 781e9d51253b6..1ac51170c94e1 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -4,7 +4,6 @@ use std::path::PathBuf; use std::{env, path::Path, process::Command}; use crate::common::{TestContext, uv_snapshot}; -use anyhow::Result; use assert_cmd::assert::OutputAssertExt; use assert_fs::{ assert::PathAssert, @@ -3656,7 +3655,7 @@ fn python_install_build_version_pypy() { /// . #[cfg(unix)] #[test] -fn missing_python_home_error_message() -> Result<()> { +fn missing_python_home_error_message() -> anyhow::Result<()> { let context = TestContext::new("3.12"); // Create a Python project so we can use `uv sync`