pyodide/pyodide-build/pyodide_build/xbuildenv_releases.py

160 lines
5.1 KiB
Python

import pydantic
from packaging.version import Version
from pydantic import BaseModel
class CrossBuildEnvReleaseSpec(BaseModel):
# The version of the Pyodide
version: str
# The URL to the cross-build environment tarball
url: str
# The SHA256 hash of the cross-build environment tarball
sha256: str | None = None
# The version of the Python interpreter
python_version: str
# The version of the Emscripten SDK
emscripten_version: str
# Minimum and maximum pyodide-build versions that is compatible with this release
min_pyodide_build_version: str | None = None
max_pyodide_build_version: str | None = None
class Config:
extra = pydantic.Extra.forbid
title = "CrossBuildEnvReleasesSpec"
@property
def python_version_tuple(self) -> tuple[int, int, int]:
v = Version(self.python_version)
return (v.major, v.minor, v.micro)
@property
def emscripten_version_tuple(self) -> tuple[int, int, int]:
v = Version(self.emscripten_version)
return (v.major, v.minor, v.micro)
def is_compatible(
self,
python_version: str | None = None,
emscripten_version: str | None = None,
pyodide_build_version: str | None = None,
) -> bool:
"""
Check if the release is compatible with the given params
Parameters
----------
python_version
The version of the Python interpreter. If None, it is not checked
emscripten_version
The version of the Emscripten SDK. If None, it is not checked
pyodide_build_version
The version of the pyodide-build. If None, it is not checked
Returns
-------
bool
True if the release is compatible with the given params, False otherwise
"""
if python_version is not None:
major, minor, _ = self.python_version_tuple
v = Version(python_version)
if major != v.major or minor != v.minor:
return False
if (
emscripten_version is not None
and self.emscripten_version != emscripten_version
):
# TODO: relax the emscripten version check
return False
if pyodide_build_version is not None:
if self.min_pyodide_build_version is not None:
if Version(pyodide_build_version) < Version(
self.min_pyodide_build_version
):
return False
if self.max_pyodide_build_version is not None:
if Version(pyodide_build_version) > Version(
self.max_pyodide_build_version
):
return False
return True
class CrossBuildEnvMetaSpec(BaseModel):
releases: dict[str, CrossBuildEnvReleaseSpec]
class Config:
extra = pydantic.Extra.forbid
title = "CrossBuildEnvMetaSpec"
description = (
"The specification for the Pyodide cross-build environment metadata"
)
def list_compatible_releases(
self,
python_version: str | None = None,
emscripten_version: str | None = None,
pyodide_build_version: str | None = None,
) -> list[CrossBuildEnvReleaseSpec]:
"""
Get the list of compatible releases
Parameters
----------
python_version
The version of the Python interpreter. If None, it is not checked
emscripten_version
The version of the Emscripten SDK. If None, it is not checked
pyodide_build_version
The version of the pyodide-build. If None, it is not checked
Returns
-------
The list of compatible releases, sorted by version number in descending order (latest first)
"""
return sorted(
[
release
for release in self.releases.values()
if release.is_compatible(
python_version, emscripten_version, pyodide_build_version
)
],
key=lambda r: Version(r.version),
reverse=True,
)
def get_latest_compatible_release(
self,
python_version: str | None = None,
emscripten_version: str | None = None,
pyodide_build_version: str | None = None,
) -> CrossBuildEnvReleaseSpec | None:
"""
Get the latest compatible release
Parameters
----------
python_version
The version of the Python interpreter. If None, it is not checked
emscripten_version
The version of the Emscripten SDK. If None, it is not checked
pyodide_build_version
The version of the pyodide-build. If None, it is not checked
Returns
-------
The latest compatible release, or None if no compatible release is found
"""
compatible_releases = self.list_compatible_releases(
python_version, emscripten_version, pyodide_build_version
)
if not compatible_releases:
return None
return compatible_releases[0]