diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bf962d..1a1f959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/micropip/_utils.py b/micropip/_utils.py index c20a364..d5ec618 100644 --- a/micropip/_utils.py +++ b/micropip/_utils.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index ee8eb4b..82acafc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/tests/test_utils.py b/tests/test_utils.py index 3be26cf..6f9e4ff 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -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: