Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

- Incompatibility errors for `pyemscripten_*` / `pyodide_*` (PEP 783) platform
wheels now report a meaningful Pyodide ABI version mismatch instead of a
garbled message such as `Wheel was built with Emscripten vpyemscripten.2026.0`.

### Changed

- `install` now raises a helpful `ValueError` when given a VCS URL
Expand Down
63 changes: 46 additions & 17 deletions micropip/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,26 +171,55 @@ def check_compatible(filename: str) -> None:
raise ValueError(f"Wheel version is invalid: {filename!r}") from None

tag: Tag = next(iter(tags))
if "emscripten" not in tag.platform:
raise ValueError(
f"Wheel platform '{tag.platform}' is not compatible with "
f"Pyodide's platform '{get_platform()}'"
)
platform = tag.platform

# PEP 783 Pyodide ABI platform tags, e.g. ``pyemscripten_2026_0_wasm32`` or
# ``pyodide_2026_0_wasm32``. These encode the Pyodide ABI version rather than
# the underlying Emscripten version, so they must be compared against the
# runtime's ABI version, not its Emscripten version.
pyodide_abi_prefixes = ("pyemscripten_", "pyodide_")
if platform.startswith(pyodide_abi_prefixes):

def platform_to_abi_version(platform: str) -> str:
for prefix in pyodide_abi_prefixes:
platform = platform.removeprefix(prefix)
return platform.removesuffix("_wasm32").replace("_", ".")

wheel_abi_version = platform_to_abi_version(platform)
runtime_abi_version = get_config_var(
"PYEMSCRIPTEN_PLATFORM_VERSION"
) or get_config_var("PYODIDE_ABI_VERSION")
if runtime_abi_version:
runtime_abi_version = runtime_abi_version.replace("_", ".")
if wheel_abi_version != runtime_abi_version:
raise ValueError(
f"Wheel was built with Pyodide ABI version {wheel_abi_version} but "
f"the current environment has Pyodide ABI version "
f"{runtime_abi_version}"
)

def platform_to_version(platform: str) -> str:
return (
platform.replace("-", "_")
.removeprefix("emscripten_")
.removesuffix("_wasm32")
.replace("_", ".")
)
elif "emscripten" in platform:

wheel_emscripten_version = platform_to_version(tag.platform)
pyodide_emscripten_version = platform_to_version(get_platform())
if wheel_emscripten_version != pyodide_emscripten_version:
def platform_to_version(platform: str) -> str:
return (
platform.replace("-", "_")
.removeprefix("emscripten_")
.removesuffix("_wasm32")
.replace("_", ".")
)

wheel_emscripten_version = platform_to_version(platform)
pyodide_emscripten_version = platform_to_version(get_platform())
if wheel_emscripten_version != pyodide_emscripten_version:
raise ValueError(
f"Wheel was built with Emscripten v{wheel_emscripten_version} but "
f"Pyodide was built with Emscripten v{pyodide_emscripten_version}"
)

else:
raise ValueError(
f"Wheel was built with Emscripten v{wheel_emscripten_version} but "
f"Pyodide was built with Emscripten v{pyodide_emscripten_version}"
f"Wheel platform '{platform}' is not compatible with "
f"Pyodide's platform '{get_platform()}'"
)

abi_incompatible = True
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ lint.select = [
"UP", # pyupgrade
"W", # pycodestyle whitespace
]
lint.ignore = [
"C901", # function is too complex
]
lint.flake8-comprehensions.allow-dict-calls-with-keyword-arguments = true
lint.mccabe.max-complexity = 13
target-version = "py312"
Expand Down
27 changes: 27 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,33 @@ def test_check_compatible_wasm32(selenium_standalone_micropip):
check_compatible(wheel_name)


@pytest.mark.parametrize("prefix", ["pyemscripten", "pyodide"])
def test_check_compatible_abi_version_mismatch(monkeypatch, prefix):
"""
A wheel built for a different Pyodide ABI version should report the ABI
version mismatch instead of garbling it into an Emscripten version.
"""
import micropip._utils as _utils
from micropip._utils import check_compatible

monkeypatch.setattr(
_utils,
"get_config_var",
lambda name: (
"2026_0"
if name in ("PYEMSCRIPTEN_PLATFORM_VERSION", "PYODIDE_ABI_VERSION")
else None
),
)

wheel_name = f"pkg-1.0.0-{CPVER}-{CPVER}-{prefix}_2025_0_wasm32.whl"
with raiseValueError(
"Wheel was built with Pyodide ABI version 2025.0 but the current "
"environment has Pyodide ABI version 2026.0"
):
check_compatible(wheel_name)


_best_tag_test_cases = (
"package, version, incompatible_tags, compatible_tags",
# Tests assume that `compatible_tags` is sorted from least to most compatible:
Expand Down
Loading