Repository: pypa/wheel

Issue:
AssertionError: would build wheel with unsupported tag ('cp38', 'cp38', 'linux_aarch64')
Experiencing this error when `pip install` _numpy_ and _cryptography_ in Docker container on Raspberry. While images built in May and June of 2020 worked well. Tried and it didn't work: 

- downgrading versions of _wheel_ and packages, 

- vice versa, upgrading _pip_, _wheel_ and packages to the very latest versions

- `pip install cryptography --no-binary cryptography`

Maybe it is an _arm64_ issue. For instance, _cryptography_ doesn't list this platform as supported https://cryptography.io/en/latest/installation/#build-on-linux

---

**Workaround**
```shell
pip install -e git+https://github.com/pyca/cryptography.git#egg=cryptography
```
---

**Details**

```shell
      File "/tmp/pip-build-env-n8ueeulb/overlay/lib/python3.8/site-packages/wheel/bdist_wheel.py", line 278, in get_tag
        assert tag in supported_tags, "would build wheel with unsupported tag {}".format(tag)
    AssertionError: would build wheel with unsupported tag ('cp38', 'cp38', 'linux_aarch64')
  ...
  ERROR: Could not build wheels for numpy which use PEP 517 and cannot be installed directly
```
OS : Raspbian GNU/Linux 10 (buster)
Docker: 19.03.12
Base: python:3.8-buster


Comments:

Patch:
diff --git a/docs/news.rst b/docs/news.rst
index 6bd84f3..9f65f03 100644
--- a/docs/news.rst
+++ b/docs/news.rst
@@ -1,6 +1,11 @@
 Release Notes
 =============
 
+**UNRELEASED**
+
+- Fixed platform tag detection for GraalPy and 32-bit python running on an aarch64
+  kernel (PR by Matthieu Darbois)
+
 **0.41.1 (2023-08-05)**
 
 - Fixed naming of the ``data_dir`` directory in the presence of local version segment
diff --git a/src/wheel/bdist_wheel.py b/src/wheel/bdist_wheel.py
index c385ebd..3ae01b3 100644
--- a/src/wheel/bdist_wheel.py
+++ b/src/wheel/bdist_wheel.py
@@ -10,6 +10,7 @@ import os
 import re
 import shutil
 import stat
+import struct
 import sys
 import sysconfig
 import warnings
@@ -57,6 +58,10 @@ setuptools_major_version = int(setuptools.__version__.split(".")[0])
 PY_LIMITED_API_PATTERN = r"cp3\d"
 
 
+def _is_32bit_interpreter():
+    return struct.calcsize("P") == 4
+
+
 def python_tag():
     return f"py{sys.version_info[0]}"
 
@@ -66,9 +71,15 @@ def get_platform(archive_root):
     result = sysconfig.get_platform()
     if result.startswith("macosx") and archive_root is not None:
         result = calculate_macosx_platform_tag(archive_root, result)
-    elif result == "linux-x86_64" and sys.maxsize == 2147483647:
-        # pip pull request #3497
-        result = "linux-i686"
+    elif _is_32bit_interpreter():
+        if result == "linux-x86_64":
+            # pip pull request #3497
+            result = "linux-i686"
+        elif result == "linux-aarch64":
+            # packaging pull request #234
+            # TODO armv8l, packaging pull request #690 => this did not land
+            # in pip/packaging yet
+            result = "linux-armv7l"
 
     return result.replace("-", "_")
 
@@ -300,11 +311,13 @@ class bdist_wheel(Command):
                 # modules, use the default platform name.
                 plat_name = get_platform(self.bdist_dir)
 
-            if (
-                plat_name in ("linux-x86_64", "linux_x86_64")
-                and sys.maxsize == 2147483647
-            ):
-                plat_name = "linux_i686"
+            if _is_32bit_interpreter():
+                if plat_name in ("linux-x86_64", "linux_x86_64"):
+                    plat_name = "linux_i686"
+                if plat_name in ("linux-aarch64", "linux_aarch64"):
+                    # TODO armv8l, packaging pull request #690 => this did not land
+                    # in pip/packaging yet
+                    plat_name = "linux_armv7l"
 
         plat_name = (
             plat_name.lower().replace("-", "_").replace(".", "_").replace(" ", "_")


Test patch:
diff --git a/tests/test_bdist_wheel.py b/tests/test_bdist_wheel.py
index b7c04b2..ee664f9 100644
--- a/tests/test_bdist_wheel.py
+++ b/tests/test_bdist_wheel.py
@@ -3,6 +3,7 @@ from __future__ import annotations
 import os.path
 import shutil
 import stat
+import struct
 import subprocess
 import sys
 import sysconfig
@@ -11,6 +12,7 @@ from unittest.mock import Mock
 from zipfile import ZipFile
 
 import pytest
+import setuptools
 
 from wheel.bdist_wheel import (
     bdist_wheel,
@@ -385,3 +387,17 @@ def test_data_dir_with_tag_build(monkeypatch, tmp_path):
         "test-1.0.dist-info/WHEEL",
     ):
         assert not_expected not in entries
+
+
+@pytest.mark.parametrize(
+    "reported,expected",
+    [("linux-x86_64", "linux_i686"), ("linux-aarch64", "linux_armv7l")],
+)
+def test_platform_linux32(reported, expected, monkeypatch):
+    monkeypatch.setattr(struct, "calcsize", lambda x: 4)
+    dist = setuptools.Distribution()
+    cmd = bdist_wheel(dist)
+    cmd.plat_name = reported
+    cmd.root_is_pure = False
+    _, _, actual = cmd.get_tag()
+    assert actual == expected
diff --git a/tests/test_macosx_libfile.py b/tests/test_macosx_libfile.py
index fed3ebb..ef8dc96 100644
--- a/tests/test_macosx_libfile.py
+++ b/tests/test_macosx_libfile.py
@@ -1,9 +1,11 @@
 from __future__ import annotations
 
 import os
-import sys
+import struct
 import sysconfig
 
+import pytest
+
 from wheel.bdist_wheel import get_platform
 from wheel.macosx_libfile import extract_macosx_min_system_version
 
@@ -214,7 +216,11 @@ class TestGetPlatformMacosx:
         assert get_platform(dylib_dir) == "macosx_11_0_x86_64"
 
 
-def test_get_platform_linux(monkeypatch):
-    monkeypatch.setattr(sysconfig, "get_platform", return_factory("linux-x86_64"))
-    monkeypatch.setattr(sys, "maxsize", 2147483647)
-    assert get_platform(None) == "linux_i686"
+@pytest.mark.parametrize(
+    "reported,expected",
+    [("linux-x86_64", "linux_i686"), ("linux-aarch64", "linux_armv7l")],
+)
+def test_get_platform_linux32(reported, expected, monkeypatch):
+    monkeypatch.setattr(sysconfig, "get_platform", return_factory(reported))
+    monkeypatch.setattr(struct, "calcsize", lambda x: 4)
+    assert get_platform(None) == expected