Add support for dynamic musl Python distributions on x86-64 Linux (#12121)

Following the upstream release and #12120, removes gating preventing
installation of the managed musl Python versions.

Of note

- The filtering of musl Python distributions has moved from the Rust
runtime to the metadata fetcher
- The filtering is now conditional on the PBS release date, removing all
old static musl distributions
- We could support the `+static` musl downloads in the future; right
now, they are deprioritized when selecting a variant
- I added test to CI which uses Alpine and installs numpy
This commit is contained in:
Zanie Blue 2025-03-11 18:14:10 -05:00 committed by GitHub
parent f3fb1c5a17
commit 553bcccb6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 52 additions and 10642 deletions

View file

@ -737,6 +737,36 @@ jobs:
eval "$(./uv generate-shell-completion bash)" eval "$(./uv generate-shell-completion bash)"
eval "$(./uvx --generate-shell-completion bash)" eval "$(./uvx --generate-shell-completion bash)"
smoke-test-linux-musl:
timeout-minutes: 10
needs: build-binary-linux-musl
name: "check system | alpine"
runs-on: ubuntu-latest
container: alpine:latest
steps:
- uses: actions/checkout@v4
- name: "Download binary"
uses: actions/download-artifact@v4
with:
name: uv-linux-musl-${{ github.sha }}
- name: "Prepare binary"
run: |
chmod +x ./uv
chmod +x ./uvx
- name: "Smoke test"
run: |
# Overwrite the 3.13.0 pin from the project, there are not functional
# musl distributions for it
./uv python pin 3.13
./uv venv -v
./uv pip install ruff -v
./uvx -v ruff --version
./uv pip install numpy -v
./uv run python -c "import numpy; print(numpy.__version__)"
smoke-test-macos: smoke-test-macos:
timeout-minutes: 10 timeout-minutes: 10
needs: build-binary-macos-x86_64 needs: build-binary-macos-x86_64

File diff suppressed because it is too large Load diff

View file

@ -61,6 +61,10 @@ import httpx
SELF_DIR = Path(__file__).parent SELF_DIR = Path(__file__).parent
VERSIONS_FILE = SELF_DIR / "download-metadata.json" VERSIONS_FILE = SELF_DIR / "download-metadata.json"
# The date at which the default CPython musl builds became dynamically linked
# instead of statically.
CPYTHON_MUSL_STATIC_RELEASE_END = 20250311
def batched(iterable: Iterable, n: int) -> Generator[tuple, None, None]: def batched(iterable: Iterable, n: int) -> Generator[tuple, None, None]:
"""Batch data into tuples of length n. The last batch may be shorter.""" """Batch data into tuples of length n. The last batch may be shorter."""
@ -142,6 +146,7 @@ class Variant(StrEnum):
@dataclass @dataclass
class PythonDownload: class PythonDownload:
release: int
version: Version version: Version
triple: PlatformTriple triple: PlatformTriple
flavor: str flavor: str
@ -253,6 +258,11 @@ class CPythonFinder(Finder):
download = self._parse_download_url(url) download = self._parse_download_url(url)
if download is None: if download is None:
continue continue
if (
download.release < CPYTHON_MUSL_STATIC_RELEASE_END
and download.triple.libc == "musl"
):
continue
logging.debug("Found %s (%s)", download.key(), download.filename) logging.debug("Found %s (%s)", download.key(), download.filename)
downloads_by_version.setdefault(download.version, []).append( downloads_by_version.setdefault(download.version, []).append(
download download
@ -341,6 +351,7 @@ class CPythonFinder(Finder):
if url.endswith(".sha256"): if url.endswith(".sha256"):
return None return None
filename = unquote(url.rsplit("/", maxsplit=1)[-1]) filename = unquote(url.rsplit("/", maxsplit=1)[-1])
release = int(url.rsplit("/")[-2])
match = self._filename_re.match(filename) or self._legacy_filename_re.match( match = self._filename_re.match(filename) or self._legacy_filename_re.match(
filename filename
@ -370,6 +381,7 @@ class CPythonFinder(Finder):
return None return None
return PythonDownload( return PythonDownload(
release=release,
version=version, version=version,
triple=triple, triple=triple,
flavor=flavor, flavor=flavor,
@ -490,6 +502,7 @@ class PyPyFinder(Finder):
platform = self._normalize_os(file["platform"]) platform = self._normalize_os(file["platform"])
libc = "gnu" if platform == "linux" else "none" libc = "gnu" if platform == "linux" else "none"
download = PythonDownload( download = PythonDownload(
release=0,
version=python_version, version=python_version,
triple=PlatformTriple( triple=PlatformTriple(
platform=platform, platform=platform,

File diff suppressed because it is too large Load diff

View file

@ -466,11 +466,7 @@ impl ManagedPythonDownload {
/// Iterate over all [`ManagedPythonDownload`]s. /// Iterate over all [`ManagedPythonDownload`]s.
pub fn iter_all() -> impl Iterator<Item = &'static ManagedPythonDownload> { pub fn iter_all() -> impl Iterator<Item = &'static ManagedPythonDownload> {
PYTHON_DOWNLOADS PYTHON_DOWNLOADS.iter()
.iter()
// TODO(konsti): musl python-build-standalone builds are currently broken (statically
// linked), so we pretend they don't exist. https://github.com/astral-sh/uv/issues/4242
.filter(|download| download.key.libc != Libc::Some(target_lexicon::Environment::Musl))
} }
pub fn url(&self) -> &'static str { pub fn url(&self) -> &'static str {

View file

@ -126,6 +126,10 @@ impl Arch {
pub fn family(&self) -> target_lexicon::Architecture { pub fn family(&self) -> target_lexicon::Architecture {
self.family self.family
} }
pub fn is_arm(&self) -> bool {
matches!(self.family, target_lexicon::Architecture::Arm(_))
}
} }
impl Display for Libc { impl Display for Libc {

View file

@ -17,7 +17,7 @@ use uv_python::downloads::{self, DownloadResult, ManagedPythonDownload, PythonDo
use uv_python::managed::{ use uv_python::managed::{
python_executable_dir, ManagedPythonInstallation, ManagedPythonInstallations, python_executable_dir, ManagedPythonInstallation, ManagedPythonInstallations,
}; };
use uv_python::platform::Libc; use uv_python::platform::{Arch, Libc};
use uv_python::{ use uv_python::{
PythonDownloads, PythonInstallationKey, PythonRequest, PythonVersionFile, PythonDownloads, PythonInstallationKey, PythonRequest, PythonVersionFile,
VersionFileDiscoveryOptions, VersionFilePreference, VersionFileDiscoveryOptions, VersionFilePreference,
@ -58,10 +58,11 @@ impl InstallRequest {
let download = match ManagedPythonDownload::from_request(&download_request) { let download = match ManagedPythonDownload::from_request(&download_request) {
Ok(download) => download, Ok(download) => download,
Err(downloads::Error::NoDownloadFound(request)) Err(downloads::Error::NoDownloadFound(request))
if request.libc().is_some_and(Libc::is_musl) => if request.libc().is_some_and(Libc::is_musl)
&& request.arch().is_some_and(Arch::is_arm) =>
{ {
return Err(anyhow::anyhow!( return Err(anyhow::anyhow!(
"uv does not yet provide musl Python distributions. See https://github.com/astral-sh/uv/issues/6890 to track support." "uv does not yet provide musl Python distributions on aarch64."
)); ));
} }
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),