Repository: python/mypy

Issue:
misc/upload-pypi.py errors when uploading wheel for platform emscripten_3_1_14_wasm32
When running `python3 misc/upload-pypi.py 0.990` there was an error when uploading wheel for platform emscripten_3_1_14_wasm32

```
Uploading mypy-0.990-cp310-cp310-win_amd64.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.7/8.7 MB • 00:00 • 274.7 MB/s
Uploading mypy-0.990-cp310-cp310-emscripten_3_1_14_wasm32.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.3/6.3 MB • 00:00 • 268.6 MB/s
WARNING  Error during upload. Retry with the --verbose option for more details.
ERROR    HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/
         Binary wheel 'mypy-0.990-cp310-cp310-emscripten_3_1_14_wasm32.whl' has an unsupported platform tag 'emscripten_3_1_14_wasm32'.
Traceback (most recent call last):
  File "/home/svalentin/src/mypy/misc/upload-pypi.py", line 139, in <module>
    main()
  File "/home/svalentin/src/mypy/misc/upload-pypi.py", line 135, in main
    upload_to_pypi(args.version, args.dry_run)
  File "/home/svalentin/src/mypy/misc/upload-pypi.py", line 123, in upload_to_pypi
    upload_dist(dist, dry_run)
  File "/home/svalentin/src/mypy/misc/upload-pypi.py", line 103, in upload_dist
    subprocess.check_call(cmd)
  File "/usr/lib/python3.9/subprocess.py", line 373, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '[PosixPath('/tmp/tmp5173rirz/venv/bin/twine'), 'upload', PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990.tar.gz'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-py3-none-any.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-win_amd64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-musllinux_1_1_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-macosx_11_0_arm64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-macosx_10_9_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp39-cp39-macosx_10_9_universal2.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-win_amd64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-musllinux_1_1_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-macosx_11_0_arm64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-macosx_10_9_universal2.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp38-cp38-macosx_10_9_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp37-cp37m-win_amd64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp37-cp37m-musllinux_1_1_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp37-cp37m-macosx_10_9_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-win_amd64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-macosx_10_9_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-win_amd64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-emscripten_3_1_14_wasm32.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-macosx_11_0_arm64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-musllinux_1_1_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-macosx_11_0_arm64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-macosx_10_9_universal2.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-macosx_10_9_universal2.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp311-cp311-musllinux_1_1_x86_64.whl'), PosixPath('/tmp/tmp8362thlv/dist/mypy-0.990-cp310-cp310-macosx_10_9_x86_64.whl')]' returned non-zero exit status 1.
```


Discussion:
I'll get more time to look into this tomorrow. Just wanted to open this in case there's anything obvious I was missing.

I think PyPI just doesn't support wasm wheels. We can simply skip this wheel I suppose.
cc @ethanhs
Yup, confirming fine to skip if it unblocks the release; we just started building WASM wheels recently
Looks like we're missing some other wheels that would be expected though: https://pypi.org/project/mypy/0.990/#files (e.g. there is only one 3.10 wheel, the previous release had 6). I'd recommend patching `upload-pypi.py` to drop the wasm wheels and re-running it to make sure we get all other wheels.
Yeah, I saw we added wasm wheel in https://github.com/python/mypy/commit/11be37863683745ec2e3df4c15440cb1c7ba44d4
I just have to modify `upload-pypi.py` to have the option to skip some platforms. Right now it just tries to upload everything it finds.
Yes, sorry, I didn't think about this, Emscripten doesn't currently have a stable ABI, so you can't upload to PyPI yet. Skipping the upload of the WASM  wheel is definitely the correct thing to do here.
Ok, I locally hacked `upload-pypi.py` to finish this release upload. Will make a proper PR later

Resolving patch:
diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py
--- a/misc/upload-pypi.py
+++ b/misc/upload-pypi.py
@@ -29,6 +29,21 @@ def is_whl_or_tar(name: str) -> bool:
     return name.endswith(".tar.gz") or name.endswith(".whl")
 
 
+def item_ok_for_pypi(name: str) -> bool:
+    if not is_whl_or_tar(name):
+        return False
+
+    if name.endswith(".tar.gz"):
+        name = name[:-7]
+    if name.endswith(".whl"):
+        name = name[:-4]
+
+    if name.endswith("wasm32"):
+        return False
+
+    return True
+
+
 def get_release_for_tag(tag: str) -> dict[str, Any]:
     with urlopen(f"{BASE}/{REPO}/releases/tags/{tag}") as f:
         data = json.load(f)
@@ -75,7 +90,7 @@ def check_sdist(dist: Path, version: str) -> None:
 
 
 def spot_check_dist(dist: Path, version: str) -> None:
-    items = [item for item in dist.iterdir() if is_whl_or_tar(item.name)]
+    items = [item for item in dist.iterdir() if item_ok_for_pypi(item.name)]
     assert len(items) > 10
     assert all(version in item.name for item in items)
     assert any(item.name.endswith("py3-none-any.whl") for item in items)
@@ -93,7 +108,7 @@ def tmp_twine() -> Iterator[Path]:
 
 def upload_dist(dist: Path, dry_run: bool = True) -> None:
     with tmp_twine() as twine:
-        files = [item for item in dist.iterdir() if is_whl_or_tar(item.name)]
+        files = [item for item in dist.iterdir() if item_ok_for_pypi(item.name)]
         cmd: list[Any] = [twine, "upload"]
         cmd += files
         if dry_run:


Test patch: