From 40efe6119bd645a061e975955139f6bde8a2f56a Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 14 Mar 2025 00:55:07 +0100 Subject: [PATCH] Fix GraalPy abi tag parsing and discovery (#12154) ## Summary There were no GraalPy binary wheels were available when uv support was added, and thus the abi tag was never tested against actual packages. Now that GraalPy publishes binary wheels to https://www.graalvm.org/python/wheels/ we noticed the abi tag was incorrect and the version info incorrectly determined. ## Test Plan I tested manually: ``` > target/debug/uv venv --python graalpy testvenv Using GraalPy 3.11.7 interpreter at: /home/tim/.pyenv/versions/graalpy-24.1.1/bin/graalpy Creating virtual environment at: testvenv Activate with: source testvenv/bin/activate > cat < uv.toml > [[index]] > url = "https://www.graalvm.org/python/wheels/" > EOF > target/debug/uv -v pip install psutil warning: Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The `[tool.uv]` section will be ignored in favor of the `uv.toml` file. DEBUG uv 0.6.6+3 (be8725553 2025-03-13) DEBUG Searching for default Python interpreter in virtual environments DEBUG Found `graalpy-3.11.7-linux-x86_64-gnu` at `/home/tim/dev/uv/.venv/bin/python3` (virtual environment) DEBUG Using Python 3.11.7 environment at: .venv DEBUG Acquired lock for `.venv` DEBUG At least one requirement is not satisfied: psutil DEBUG Using request timeout of 30s DEBUG Solving with installed Python version: 3.11.7 DEBUG Solving with target Python version: >=3.11.7 DEBUG Adding direct dependency: psutil* DEBUG Found fresh response for: https://www.graalvm.org/python/wheels/psutil/ DEBUG Searching for a compatible version of psutil (*) DEBUG Selecting: psutil==5.9.8 [compatible] (psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl) DEBUG No cache entry for: https://gds.oracle.com/download/graalpy-wheels/psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl DEBUG Tried 1 versions: psutil 1 DEBUG marker environment resolution took 0.968s Resolved 1 package in 971ms DEBUG Identified uncached distribution: psutil==5.9.8 DEBUG No cache entry for: https://gds.oracle.com/download/graalpy-wheels/psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl Prepared 1 package in 268ms Installed 1 package in 28ms + psutil==5.9.8 DEBUG Released lock at `/home/tim/dev/uv/.venv/.lock` ``` --------- Co-authored-by: Charlie Marsh --- crates/uv-platform-tags/src/abi_tag.rs | 49 +++++++------------ crates/uv-platform-tags/src/tags.rs | 2 +- .../uv-python/python/get_interpreter_info.py | 12 ++++- crates/uv/tests/it/common/mod.rs | 2 +- crates/uv/tests/it/pip_install_scenarios.rs | 2 +- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/crates/uv-platform-tags/src/abi_tag.rs b/crates/uv-platform-tags/src/abi_tag.rs index 2ea5c872e..70b11d8c2 100644 --- a/crates/uv-platform-tags/src/abi_tag.rs +++ b/crates/uv-platform-tags/src/abi_tag.rs @@ -34,7 +34,7 @@ pub enum AbiTag { python_version: Option<(u8, u8)>, implementation_version: (u8, u8), }, - /// Ex) `graalpy310_graalpy240_310_native` + /// Ex) `graalpy240_310_native` GraalPy { python_version: (u8, u8), implementation_version: (u8, u8), @@ -109,7 +109,7 @@ impl std::fmt::Display for AbiTag { } => { write!( f, - "graalpy{py_major}{py_minor}_graalpy{impl_major}{impl_minor}_{py_major}{py_minor}_native" + "graalpy{impl_major}{impl_minor}_{py_major}{py_minor}_native" ) } Self::Pyston { @@ -237,29 +237,21 @@ impl FromStr for AbiTag { }) } } else if let Some(rest) = s.strip_prefix("graalpy") { - // Ex) `graalpy310_graalpy240_310_native` - let version_end = rest - .find('_') - .ok_or_else(|| ParseAbiTagError::InvalidFormat { - implementation: "GraalPy", - tag: s.to_string(), - })?; - let version_str = &rest[..version_end]; - let (major, minor) = parse_python_version(version_str, "GraalPy", s)?; - let rest = rest[version_end + 1..] - .strip_prefix("graalpy") - .ok_or_else(|| ParseAbiTagError::InvalidFormat { - implementation: "GraalPy", - tag: s.to_string(), - })?; - let version_end = rest - .find('_') - .ok_or_else(|| ParseAbiTagError::InvalidFormat { - implementation: "GraalPy", - tag: s.to_string(), - })?; - let rest = &rest[..version_end]; - let (impl_major, impl_minor) = parse_impl_version(rest, "GraalPy", s)?; + // Ex) `graalpy240_310_native` + let (impl_ver_str, rest) = + rest.split_once('_') + .ok_or_else(|| ParseAbiTagError::InvalidFormat { + implementation: "GraalPy", + tag: s.to_string(), + })?; + let (impl_major, impl_minor) = parse_impl_version(impl_ver_str, "GraalPy", s)?; + let (py_ver_str, _) = + rest.split_once('_') + .ok_or_else(|| ParseAbiTagError::InvalidFormat { + implementation: "GraalPy", + tag: s.to_string(), + })?; + let (major, minor) = parse_python_version(py_ver_str, "GraalPy", s)?; Ok(Self::GraalPy { python_version: (major, minor), implementation_version: (impl_major, impl_minor), @@ -434,11 +426,8 @@ mod tests { python_version: (3, 10), implementation_version: (2, 40), }; - assert_eq!( - AbiTag::from_str("graalpy310_graalpy240_310_native"), - Ok(tag) - ); - assert_eq!(tag.to_string(), "graalpy310_graalpy240_310_native"); + assert_eq!(AbiTag::from_str("graalpy240_310_native"), Ok(tag)); + assert_eq!(tag.to_string(), "graalpy240_310_native"); assert_eq!( AbiTag::from_str("graalpy310"), diff --git a/crates/uv-platform-tags/src/tags.rs b/crates/uv-platform-tags/src/tags.rs index 2a85a3114..22ad12dbf 100644 --- a/crates/uv-platform-tags/src/tags.rs +++ b/crates/uv-platform-tags/src/tags.rs @@ -395,7 +395,7 @@ impl Implementation { python_version: Some(python_version), implementation_version, }, - // Ex) `graalpy310_graalpy240_310_native + // Ex) `graalpy240_310_native Self::GraalPy => AbiTag::GraalPy { python_version, implementation_version, diff --git a/crates/uv-python/python/get_interpreter_info.py b/crates/uv-python/python/get_interpreter_info.py index c134a0a6b..2b1ee09cc 100644 --- a/crates/uv-python/python/get_interpreter_info.py +++ b/crates/uv-python/python/get_interpreter_info.py @@ -34,8 +34,18 @@ if sys.version_info[0] < 3: sys.exit(0) if hasattr(sys, "implementation"): - implementation_version = format_full_version(sys.implementation.version) implementation_name = sys.implementation.name + if implementation_name == "graalpy": + # GraalPy reports the CPython version as sys.implementation.version, + # so we need to discover the GraalPy version from the cache_tag + import re + implementation_version = re.sub( + r"graalpy(\d)(\d+)-\d+", + r"\1.\2", + sys.implementation.cache_tag + ) + else: + implementation_version = format_full_version(sys.implementation.version) else: implementation_version = "0" implementation_name = "" diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs index 6ff533cad..f7b0a0b62 100644 --- a/crates/uv/tests/it/common/mod.rs +++ b/crates/uv/tests/it/common/mod.rs @@ -32,7 +32,7 @@ use uv_static::EnvVars; // Exclude any packages uploaded after this date. static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z"; -pub const PACKSE_VERSION: &str = "0.3.45"; +pub const PACKSE_VERSION: &str = "0.3.46"; /// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests /// to prevent dependency confusion attacks against our test suite. diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs index fa422c466..41008cdb4 100644 --- a/crates/uv/tests/it/pip_install_scenarios.rs +++ b/crates/uv/tests/it/pip_install_scenarios.rs @@ -4091,7 +4091,7 @@ fn no_sdist_no_wheels_with_matching_abi() { ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching Python ABI tag (e.g., `cp38`), we can conclude that all versions of package-a cannot be used. And because you require package-a, we can conclude that your requirements are unsatisfiable. - hint: You require CPython 3.8 (`cp38`), but we only found wheels for `package-a` (v1.0.0) with the following Python ABI tag: `graalpy310_graalpy240_310_native` + hint: You require CPython 3.8 (`cp38`), but we only found wheels for `package-a` (v1.0.0) with the following Python ABI tag: `graalpy240_310_native` "###); assert_not_installed(