😸

Mac で`xxx.whl is not a supported wheel on this platform.` を解決する

2022/03/11に公開

tldr;

from pip._vendor.packaging.tags import mac_platforms; list(mac_platforms)
した結果から選ぶ

['macosx_12_0_arm64',
 'macosx_12_0_universal2',
 'macosx_11_0_arm64',
 ...]

やりたいこと: .whl の名前を正しくする

python setup.py bdist-wheel した時に <my-package>-py3-none-any.whl ができてこれを twine upload などで pypi にあげるが、これを linux と mac で分けたかった時にハマったので忘備録.

なぜ必要か

Rust の CLI アプリをラップしていたので、build は各OS上でやらないと segfault になる.

やったこと

解決に至った道のり.

Step1: --plat-name の設定

python setup.py bdist_wheel --plat-name=<my-platform-name> すると <my-package>-py3-none-<my-platform-name>.whl ができるので、適当に macosx と名前をつけてみた.<my-package>-maxosx.whl は正しくできるが、それを pip install ***.whl しようとするとxxx.whl is not a supported wheel on this platform.が出る.名前だけ anyに変えるとインストールできるので名前だけの問題ぽい.

Step2: pip のソースコードから該当エラーを探す

ググってもあまり埒が開かなかったのでpip のソースコード(https://github.com/pypa/pip)を見に行く.手元に clone して git grep 'is not a supported wheel on this platform' すると3箇所だけヒットする.

src/pip/_internal/req/req_set.py:92:                    "{} is not a supported wheel on this platform.".format(
src/pip/_internal/resolution/resolvelib/factory.py:140:        msg = f"{link.filename} is not a supported wheel on this platform."
tests/functional/test_install_reqs.py:757:        "simple.dist-0.1-py1-none-invalid.whl is not a supported wheel on this platform"

test case

一番下がテストなのでtests/functional/test_install_reqs.py を覗くと今回の挙動がちゃんとテストされている.やはりどこかで名前の validation がされていると考えて良さそう.

validation

一番上のsrc/pip/_internal/req/req_set.pyが validation されている部分.

# src/pip/_internal/req/req_set.py
from pip._internal.utils import compatibility_tags

	    ...
            tags = compatibility_tags.get_supported()
            if self.check_supported_wheels and not wheel.supported(tags):
                raise InstallationError(
                    "{} is not a supported wheel on this platform.".format(
                        wheel.filename
                    )
                )

compatibility_tags.get_supported() の中身を探していけば良さそう.

Step3: compatibility_tags の中身を読み解いていく

src/pip/_internal/utils/compatibility_tags.py の中身を見ていく.
_mac_platforms といういかにもな関数があるものの、arch を事前に指定しているのでこれを呼び出す他の関数がありそう.タグの生成をしているところを見にいく.

# src/pip/_internal/utils/compatibility_tags.py
from pip._vendor.packaging.tags import (
    ...,
    compatible_tags,
    
    ...
    supported.extend(
        compatible_tags(
            python_version=python_version,
            interpreter=interpreter,
            platforms=platforms,
        )
    )

Step4:

importされているsrc/pip/_vendor/packaging/tags.pyを見ていく.platform_tags が mac_platforms を呼び出しているので、この辺りで 適当なintrepretor を開いて中身を見てみる.

def mac_platforms(
    version: Optional[MacVersion] = None, arch: Optional[str] = None
) -> Iterator[str]:
    """
    Yields the platform tags for a macOS system.
    The `version` parameter is a two-item tuple specifying the macOS version to
    generate platform tags for. The `arch` parameter is the CPU architecture to
    generate platform tags for. Both parameters default to the appropriate value
    for the current system.
    """
    version_str, _, cpu_arch = platform.mac_ver()
    if version is None:
        version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
    else:
        version = version
    if arch is None:
        arch = _mac_arch(cpu_arch)
    else:
        arch = arch

    if (10, 0) <= version and version < (11, 0):
    ...

何となくそれっぽい.すると以下のリストが出てくる

> from pip._vendor.packaging.tags import mac_platforms
> list(mac_platforms)
['macosx_12_0_arm64',
 'macosx_12_0_universal2',
 'macosx_11_0_arm64',
 ...]

--plat-names=macosx_12_0_universal2 とすると無事に .whl をインストールできた.

Discussion