Repository: pypa/cibuildwheel

Issue:
PyPy has released bug fixes and a python3.9
### Description

PyPy released version v7.3.8. It would be nice to be able to use it in cibuildwheel, including the newly released python3.9

### Build log

_No response_

### CI config

_No response_


Discussion:
Bug fixes are included in #1027 (for macOS/Windows).
PyPy 3.9 is addressed in #1031 (for macOS/Windows). 
~~manylinux is on hold for both c.f. https://github.com/pypa/manylinux/pull/1288#issuecomment-1046322172~~

Resolving patch:
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -109,7 +109,12 @@ def __init__(self, arch_str: ArchStr):
         response = requests.get("https://downloads.python.org/pypy/versions.json")
         response.raise_for_status()
 
-        releases = [r for r in response.json() if r["pypy_version"] != "nightly"]
+        releases = [
+            r
+            for r in response.json()
+            if r["pypy_version"] != "nightly"
+            and f'{r["python_version"]}-{r["pypy_version"]}' != "3.7.12-7.3.8"
+        ]
         for release in releases:
             release["pypy_version"] = Version(release["pypy_version"])
             release["python_version"] = Version(release["python_version"])


Test patch:
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -71,6 +71,9 @@ def test(manylinux_image, tmp_path):
     if manylinux_image in {"manylinux1"}:
         # We don't have a manylinux1 image for PyPy & CPython 3.10 and above
         add_env["CIBW_SKIP"] = "pp* cp31*"
+    if manylinux_image in {"manylinux2010"}:
+        # We don't have a manylinux2010 image for PyPy 3.9
+        add_env["CIBW_SKIP"] = "pp39*"
 
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
 
@@ -88,4 +91,7 @@ def test(manylinux_image, tmp_path):
     if manylinux_image in {"manylinux1"}:
         # remove PyPy & CPython 3.10 and above
         expected_wheels = [w for w in expected_wheels if "-pp" not in w and "-cp31" not in w]
+    if manylinux_image in {"manylinux2010"}:
+        # remove PyPy 3.9
+        expected_wheels = [w for w in expected_wheels if "-pp39" not in w]
     assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/utils.py b/test/utils.py
--- a/test/utils.py
+++ b/test/utils.py
@@ -135,7 +135,7 @@ def expected_wheels(
     python_abi_tags = ["cp36-cp36m", "cp37-cp37m", "cp38-cp38", "cp39-cp39", "cp310-cp310"]
 
     if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
-        python_abi_tags += ["pp37-pypy37_pp73", "pp38-pypy38_pp73"]
+        python_abi_tags += ["pp37-pypy37_pp73", "pp38-pypy38_pp73", "pp39-pypy39_pp73"]
 
     if platform == "macos" and machine_arch == "arm64":
         # currently, arm64 macs are only supported by cp39 & cp310
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -11,7 +11,7 @@
 from cibuildwheel import linux, util
 from cibuildwheel.__main__ import main
 
-ALL_IDS = {"cp36", "cp37", "cp38", "cp39", "cp310", "pp37", "pp38"}
+ALL_IDS = {"cp36", "cp37", "cp38", "cp39", "cp310", "pp37", "pp38", "pp39"}
 
 
 @pytest.fixture
@@ -133,7 +133,7 @@ def test_build_with_override_launches(mock_build_docker, monkeypatch, tmp_path):
 
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
     assert identifiers == {
-        f"{x}-manylinux_x86_64" for x in ALL_IDS - {"cp36", "cp310", "pp37", "pp38"}
+        f"{x}-manylinux_x86_64" for x in ALL_IDS - {"cp36", "cp310", "pp37", "pp38", "pp39"}
     }
     assert kwargs["options"].build_options("cp37-manylinux_x86_64").before_all == ""
 
@@ -146,6 +146,7 @@ def test_build_with_override_launches(mock_build_docker, monkeypatch, tmp_path):
         "cp310-manylinux_x86_64",
         "pp37-manylinux_x86_64",
         "pp38-manylinux_x86_64",
+        "pp39-manylinux_x86_64",
     }
 
     kwargs = build_on_docker.call_args_list[3][1]