Skip to content

Commit 1bee45b

Browse files
committed
Show a better hint for broken PBS venvs
For some reason, I'm encountering broken PBS venvs again (astral-sh/python-build-standalone#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 <prefix> Could not find platform dependent libraries <exec_prefix> 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): <no Python frame> ``` ``` $ 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 (astral-sh/python-build-standalone#380). You can recreate the virtual environment with `uv venv`. ```
1 parent 885ac9d commit 1bee45b

File tree

2 files changed

+80
-15
lines changed

2 files changed

+80
-15
lines changed

crates/uv-python/src/interpreter.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,27 @@ pub enum InterpreterInfoError {
903903
},
904904
#[error("Only Pyodide is support for Emscripten Python")]
905905
EmscriptenNotPyodide,
906+
#[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())]
907+
PythonHomeNotFound,
908+
}
909+
910+
impl InterpreterInfoError {
911+
/// Check whether the stderr of `python` matches a known pattern.
912+
pub(crate) fn from_query_stderr(stderr: &str) -> Option<Self> {
913+
// If the Python version is too old, we may not even be able to invoke the query script
914+
if stderr.contains("Unknown option: -I") {
915+
return Some(Self::UnsupportedPython);
916+
}
917+
918+
// Until we fixed the PBS bug, inform the user that this is bug on our side and can be fixed
919+
// with `uv venv`.
920+
// https://github.com/astral-sh/python-build-standalone/issues/380
921+
if stderr.contains("ModuleNotFoundError: No module named 'encodings'") {
922+
return Some(Self::PythonHomeNotFound);
923+
}
924+
925+
None
926+
}
906927
}
907928

908929
#[allow(clippy::struct_excessive_bools)]
@@ -979,10 +1000,9 @@ impl InterpreterInfo {
9791000
if !output.status.success() {
9801001
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
9811002

982-
// If the Python version is too old, we may not even be able to invoke the query script
983-
if stderr.contains("Unknown option: -I") {
1003+
if let Some(query_error) = InterpreterInfoError::from_query_stderr(&stderr) {
9841004
return Err(Error::QueryScript {
985-
err: InterpreterInfoError::UnsupportedPython,
1005+
err: query_error,
9861006
path: interpreter.to_path_buf(),
9871007
});
9881008
}
@@ -999,20 +1019,19 @@ impl InterpreterInfo {
9991019
serde_json::from_slice(&output.stdout).map_err(|err| {
10001020
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
10011021

1002-
// If the Python version is too old, we may not even be able to invoke the query script
1003-
if stderr.contains("Unknown option: -I") {
1004-
Error::QueryScript {
1005-
err: InterpreterInfoError::UnsupportedPython,
1006-
path: interpreter.to_path_buf(),
1007-
}
1008-
} else {
1009-
Error::UnexpectedResponse(UnexpectedResponseError {
1010-
err,
1011-
stdout: String::from_utf8_lossy(&output.stdout).trim().to_string(),
1012-
stderr,
1022+
if let Some(query_error) = InterpreterInfoError::from_query_stderr(&stderr) {
1023+
return Error::QueryScript {
1024+
err: query_error,
10131025
path: interpreter.to_path_buf(),
1014-
})
1026+
};
10151027
}
1028+
1029+
Error::UnexpectedResponse(UnexpectedResponseError {
1030+
err,
1031+
stdout: String::from_utf8_lossy(&output.stdout).trim().to_string(),
1032+
stderr,
1033+
path: interpreter.to_path_buf(),
1034+
})
10161035
})?;
10171036

10181037
match result {

crates/uv/tests/it/python_install.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::path::PathBuf;
44
use std::{env, path::Path, process::Command};
55

66
use crate::common::{TestContext, uv_snapshot};
7+
use anyhow::Result;
78
use assert_cmd::assert::OutputAssertExt;
89
use assert_fs::{
910
assert::PathAssert,
@@ -3650,3 +3651,48 @@ fn python_install_build_version_pypy() {
36503651
error: No download found for request: pypy-3.10-[PLATFORM]
36513652
");
36523653
}
3654+
3655+
/// Show a fitting error message for
3656+
/// <https://github.com/astral-sh/python-build-standalone/issues/380>.
3657+
#[cfg(unix)]
3658+
#[test]
3659+
fn missing_python_home_error_message() -> Result<()> {
3660+
let context = TestContext::new("3.12");
3661+
3662+
// Create a Python project so we can use `uv sync`
3663+
context.init().assert().success();
3664+
3665+
// Create a broken venv from a symlink.
3666+
let sys_executable = fs_err::canonicalize(context.venv.join("bin").join("python"))?;
3667+
fs_err::os::unix::fs::symlink(sys_executable, context.temp_dir.join("python-link"))?;
3668+
fs_err::remove_dir_all(context.venv.as_ref())?;
3669+
Command::new(context.temp_dir.join("python-link"))
3670+
.arg("-m")
3671+
.arg("venv")
3672+
.arg("--without-pip")
3673+
.arg(context.venv.as_ref())
3674+
.assert()
3675+
.success();
3676+
3677+
uv_snapshot!(context.filters(), context.pip_list().arg("-p").arg(context.venv.join("bin").join("python")), @r"
3678+
success: false
3679+
exit_code: 2
3680+
----- stdout -----
3681+
3682+
----- stderr -----
3683+
error: Failed to inspect Python interpreter from provided path at `.venv/bin/python`
3684+
Caused by: Can't use Python at `[VENV]/bin/python`
3685+
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`.
3686+
");
3687+
3688+
// By default, we skip broken interpreters
3689+
uv_snapshot!(context.filters(), context.pip_list(), @r"
3690+
success: true
3691+
exit_code: 0
3692+
----- stdout -----
3693+
3694+
----- stderr -----
3695+
");
3696+
3697+
Ok(())
3698+
}

0 commit comments

Comments
 (0)