Skip to content

Commit b3fc247

Browse files
Merge pull request #367 from Blosc/pyodide
Support for building WASM32 wheels. There is a need to get rid of `httpx` in favor of `requests`, but that can come later.
2 parents 1c0732c + 74286eb commit b3fc247

17 files changed

Lines changed: 308 additions & 132 deletions

.github/workflows/wasm.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Python wheels for WASM
2+
on:
3+
push:
4+
tags:
5+
- '*'
6+
pull_request:
7+
branches:
8+
- main
9+
10+
env:
11+
CIBW_BUILD_VERBOSITY: 1
12+
# cibuildwheel cannot choose for a specified version of pyodide yet
13+
# PYODIDE_VERSION: 0.27.2
14+
15+
jobs:
16+
build_wheels_wasm:
17+
name: Build and test wheels for WASM on ${{ matrix.os }} for ${{ matrix.p_ver }}
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: write
21+
env:
22+
CIBW_BUILD: ${{ matrix.cibw_build }}
23+
CMAKE_ARGS: "-DWITH_OPTIM=OFF"
24+
CIBW_TEST_COMMAND: "pytest {project}/tests/ndarray/test_reductions.py"
25+
strategy:
26+
matrix:
27+
os: [ubuntu-latest]
28+
cibw_build: ["cp3{11,12,13}-*"]
29+
p_ver: ["3.11-3.13"]
30+
31+
steps:
32+
- name: Checkout repo
33+
uses: actions/checkout@v4
34+
35+
- name: Set up Python
36+
uses: actions/setup-python@v5
37+
with:
38+
python-version: '3.x'
39+
40+
- name: Install dependencies
41+
run: |
42+
sudo apt-get update
43+
sudo apt-get install -y cmake
44+
45+
- name: Install cibuildwheel
46+
run: pip install cibuildwheel
47+
48+
- name: Build wheels
49+
# Testing is automaticall made by cibuildwheel
50+
run: cibuildwheel --platform pyodide
51+
52+
- name: Upload wheels
53+
uses: actions/upload-artifact@v4
54+
with:
55+
name: wheels-wasm-${{ matrix.os }}-${{ matrix.p_ver }}
56+
path: ./wheelhouse/*.whl
57+
58+
- name: Upload wheel to release
59+
if: startsWith(github.ref, 'refs/tags/')
60+
env:
61+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62+
run: |
63+
gh release upload ${GITHUB_REF_NAME} ./wheelhouse/*.whl

pyproject.toml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ classifiers = [
3131
requires-python = ">=3.11"
3232
# Follow guidelines from https://scientific-python.org/specs/spec-0000/
3333
dependencies = [
34-
"numpy>=1.25.0",
34+
"numpy>=1.26",
35+
#"numpy>=2",
3536
"ndindex",
3637
"msgpack",
37-
"numexpr",
38-
"py-cpuinfo",
39-
"httpx",
4038
"platformdirs",
39+
"numexpr; platform_machine != 'wasm32'",
40+
"py-cpuinfo; platform_machine != 'wasm32'",
41+
"httpx; platform_machine != 'wasm32'",
4142
]
4243
version = "3.1.2.dev0"
4344

@@ -59,8 +60,8 @@ dev = [
5960
]
6061
test = [
6162
"pytest",
62-
"psutil",
63-
"torch",
63+
"psutil; platform_machine != 'wasm32'",
64+
"torch; platform_machine != 'wasm32'",
6465
]
6566
doc = [
6667
"sphinx>=8",
@@ -81,8 +82,11 @@ build-verbosity = 1
8182
# Skip unsupported python versions as well as 32-bit platforms, which are not supported anymore.
8283
skip = "*-manylinux_i686 cp*-win32 *_ppc64le *_s390x *musllinux*"
8384
# We won't require torch when testing wheels to avoid building/running torch on slow platforms
84-
test-requires = "pytest psutil"
85-
test-command = "pytest {project}/tests"
85+
#test-requires = "pytest psutil"
86+
test-requires = "pytest"
87+
#test-command = "pytest {project}/tests" # default command
88+
# Use a simpler command here, and let the workflow .yml file to set the command
89+
test-command = "python -c \"import blosc2; blosc2.print_versions()\""
8690
# Manylinux 2014 will be the default for x86_64 and aarch64
8791
manylinux-x86_64-image = "manylinux2014"
8892
manylinux-aarch64-image = "manylinux2014"

src/blosc2/__init__.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@
1010
# ruff: noqa: E402 - Module level import not at top of file
1111
# ruff: noqa: F401 - `var` imported but unused
1212

13+
import platform
1314
from enum import Enum
1415

15-
import numexpr
16+
# Do the platform check once at module level
17+
IS_WASM = platform.machine() == "wasm32"
18+
# IS_WASM = True # for testing (comment this line out for production)
19+
"""
20+
Flag for WebAssembly platform.
21+
"""
22+
23+
if not IS_WASM:
24+
import numexpr
1625

1726
from .version import __version__
1827

@@ -200,7 +209,9 @@ class Tuner(Enum):
200209
# Experiments say that, when using a large number of threads, it is better to not use them all
201210
if nthreads > 16:
202211
nthreads -= nthreads // 8
203-
numexpr.set_num_threads(nthreads)
212+
if not IS_WASM:
213+
# WASM does not support threading
214+
numexpr.set_num_threads(nthreads)
204215

205216
# This import must be before ndarray and schunk
206217
from .storage import ( # noqa: I001

src/blosc2/c2array.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
if TYPE_CHECKING:
1616
from collections.abc import Sequence
1717

18-
import httpx
1918
import numpy as np
2019

2120
import blosc2
2221

22+
if not blosc2.IS_WASM:
23+
import httpx
24+
2325
_subscriber_data = {
2426
"urlbase": os.environ.get("BLOSC_C2URLBASE"),
2527
"auth_token": "",

src/blosc2/core.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424
from functools import lru_cache
2525
from typing import TYPE_CHECKING, Any
2626

27-
import cpuinfo
2827
import numpy as np
2928
import platformdirs
3029

3130
import blosc2
3231
from blosc2 import blosc2_ext
3332

33+
if not blosc2.IS_WASM:
34+
import cpuinfo
35+
3436
if TYPE_CHECKING:
3537
from collections.abc import Callable
3638

@@ -1119,13 +1121,19 @@ def print_versions():
11191121
for clib in sorted(clib_versions.keys()):
11201122
print(f" {clib}: {clib_versions[clib]}")
11211123
print(f"NumPy version: {np.__version__}")
1124+
if not blosc2.IS_WASM:
1125+
import numexpr
1126+
1127+
print(f"numexpr version: {numexpr.__version__}")
11221128
print(f"Python version: {sys.version}")
11231129
(sysname, _nodename, release, version, machine, processor) = platform.uname()
11241130
print(f"Platform: {sysname}-{release}-{machine} ({version})")
11251131
if sysname == "Linux":
11261132
distro = os_release_pretty_name()
11271133
if distro:
11281134
print(f"Linux dist: {distro}")
1135+
if blosc2.IS_WASM:
1136+
processor = "wasm32"
11291137
if not processor:
11301138
processor = "not recognized"
11311139
print(f"Processor: {processor}")
@@ -1202,6 +1210,17 @@ def linux_cache_size(cache_level: int, default_size: int) -> int:
12021210

12031211

12041212
def _get_cpu_info():
1213+
if blosc2.IS_WASM:
1214+
# Emscripten/wasm32 does not have access to CPU information.
1215+
# Populate it with some reasonable defaults.
1216+
return {
1217+
"brand": "Emscripten",
1218+
"arch": "wasm32",
1219+
"count": 1,
1220+
"l1_data_cache_size": 32 * 1024,
1221+
"l2_cache_size": 256 * 1024,
1222+
"l3_cache_size": 1024 * 1024,
1223+
}
12051224
cpu_info = cpuinfo.get_cpu_info()
12061225
# cpuinfo does not correctly retrieve the cache sizes for Apple Silicon, so do it manually
12071226
if platform.system() == "Darwin":

0 commit comments

Comments
 (0)