From 241ad88051bd7f164e050dcd742de44db9a2abf2 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 3 Oct 2025 14:36:08 -0500 Subject: [PATCH] Confirm that the directory name is a valid Python install key during managed check (#16080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/astral-sh/uv/issues/16077 ``` ❯ UV_PYTHON_INSTALL_DIR=/opt uv python list --no-managed-python cpython-3.9.6-macos-aarch64-none /usr/bin/python3 ❯ alias uv=$(pwd)/target/debug/uv ❯ UV_PYTHON_INSTALL_DIR=/opt uv python list --no-managed-python cpython-3.13.3-macos-aarch64-none /opt/homebrew/bin/python3.13 -> ../Cellar/python@3.13/3.13.3/bin/python3.13 cpython-3.13.3-macos-aarch64-none /opt/homebrew/bin/python3 -> ../Cellar/python@3.13/3.13.3/bin/python3 cpython-3.12.10-macos-aarch64-none /opt/homebrew/bin/python3.12 -> ../Cellar/python@3.12/3.12.10/bin/python3.12 cpython-3.11.12-macos-aarch64-none /opt/homebrew/bin/python3.11 -> ../Cellar/python@3.11/3.11.12/bin/python3.11 cpython-3.9.6-macos-aarch64-none /usr/bin/python3 pypy-3.10.14-macos-aarch64-none /opt/homebrew/bin/pypy3 -> ../Cellar/pypy3.10/7.3.17_1/bin/pypy3 ``` Prevents false positives when the `UV_PYTHON_INSTALL_DIR` is a prefix of some other path with Python installations --- crates/uv-python/src/interpreter.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 0d1a5084a..644215f10 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -3,6 +3,7 @@ use std::env::consts::ARCH; use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; +use std::str::FromStr; use std::sync::OnceLock; use std::{env, io}; @@ -301,7 +302,19 @@ impl Interpreter { return false; }; - self.sys_base_prefix.starts_with(installations.root()) + let Ok(suffix) = self.sys_base_prefix.strip_prefix(installations.root()) else { + return false; + }; + + let Some(first_component) = suffix.components().next() else { + return false; + }; + + let Some(name) = first_component.as_os_str().to_str() else { + return false; + }; + + PythonInstallationKey::from_str(name).is_ok() } /// Returns `Some` if the environment is externally managed, optionally including an error