. This is 2026. Python 3.13 doesn’t ship setuptools in the stdlib anymore, and setuptools 82 dropped pkg_resources entirely. uvloop’s setup.py imports pkg_resources at the top level. Fix: pin setuptools<81 in the build environment.
Attempt 2: Same error. Wait, what? The fix was in the workflow file. But pip wheel creates an isolated build environment. Inside that sandbox, it installs the latest setuptools (82+), which doesn’t have pkg_resources. My pinned version in the outer environment was irrelevant. Fix: switch from pip wheel to python3 setup.py bdist_wheel, which uses the ambient environment.
Attempt 3: Same error. Again. I stared at the workflow diff. The fix was there. Then I realized: github-act-runner caches workflow definitions. The runner was executing the old version from before my fix. I had to wait for the cache to expire or restart the runner service.
Attempt 4: Progress! The wheel compiled successfully. All 18 minutes of C code crunching through GCC. Then: cp: cannot create regular file '/tmp/wheels/': Not a directory. The output directory didn’t exist. Fix: mkdir -p /tmp/wheels.
Attempt 5: Success. Runner #2 (the one that had been sitting idle) finally built uvloop 0.22.1.
Five attempts. The actual RISC-V compilation was never the problem. Not once. It was setuptools dropping APIs, pip’s build isolation fighting legacy setup.py scripts, CI runner caching stale workflows, and a missing mkdir. The Python packaging ecosystem is in transition, and the rough edges show up in places you don’t expect.
A Surprising Discovery: ISA Compatibility
I was worried that wheels built on RVV-capable hardware (the SpacemiT K1 has full RVV 1.0) would require vector extensions to run. That would mean separate builds for boards with and without RVV.
I checked the ELF attributes of the compiled shared objects:
Tag_RISCV_arch: "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0"
No vector extensions baked in. GCC targets baseline rv64gc by default, regardless of what the host CPU supports. These wheels run on any riscv64 Linux system: boards with RVV, boards without it, even QEMU. One build to rule them all.
The Result
Twenty-five native riscv64 wheels. A PEP 503 index on GitHub Pages. Automated detection of new upstream versions. Two BananaPi F3 boards doing the building. One --extra-index-url flag away from instant installs.
What used to take hours of compilation on every riscv64 machine now takes seconds:
pip install tokenizers pydantic-core safetensors tiktoken \
--extra-index-url https://gounthar.github.io/riscv64-python-wheels/simple/
Obviously these forks aren’t going to stick around forever — long-term, these packages should add riscv64 to their own cibuildwheel matrices. I’m planning to open upstream PRs for that. But for now, the wheel factory keeps running.
>
>
> Note: A word of caution: --extra-index-url checks both PyPI and the extra index, which means a malicious package with a higher version on PyPI could shadow ours. For maximum safety, use --extra-index-url with --only-binary :all: to avoid pulling unexpected source packages.
>
>
What happened next? I tried the index on a clean machine and everything broke. That story is in part two: The Dependency Rabbit Hole: When 25 RISC-V Python Wheels Weren’t Enough.
Takeaways
PEP 503 index URLs must be absolute when your wheels live on a different domain than your index. Relative URLs will silently 404. _ _`github-act-runner` is the only game in town* for riscv64 GitHub Actions, but expect quirks: set GH_REPO explicitly, always add actions/checkout, and don’t restart the service during builds.
Python’s packaging transition bites in CI: setuptools 82 dropped pkg_resources, pip’s build isolation ignores your outer environment, and legacy setup.py scripts haven’t caught up. Pin your build tools.
Batch your CI fixes: when you maintain 26 forks, a one-line fix means 26 PRs. Script it or lose your mind.
Cross-repo dispatch needs a PAT: GITHUB_TOKEN can’t trigger workflows in other repos. Create a PAT with repo + workflow scopes and set it as DISPATCH_TOKEN in every fork.
riscv64 wheels target baseline rv64gc by default — no vector extensions required. One build works on all riscv64 Linux systems.
The compiler is not the problem. Seriously. Every single failure I hit was packaging, build isolation, or CI plumbing. GCC and Rust compiled everything I threw at them without complaining once.
The PEP 503 index is live at . The source and automation live at . If you’re running Python on RISC-V, try it out.
\