Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build and test PyWavelets Pyodide wheels in CI #701

Merged
merged 30 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0226404
Add some relevant files and venvs to ignore
agriyakhetarpal Feb 20, 2024
a05ab89
Clarify + assert correct Python version lower bounds
agriyakhetarpal Feb 20, 2024
0580579
Add workflow for testing WASM distribution
agriyakhetarpal Feb 20, 2024
a2ed067
For now, skip tests that use `concurrent.futures`
agriyakhetarpal Feb 20, 2024
198fc92
Enable coloured output for GitHub Actions
agriyakhetarpal Feb 21, 2024
80ae120
Use different check for Emscripten Python interpreter
agriyakhetarpal Feb 21, 2024
7687665
Rename `ImportError` error message for `batch_processing` module
agriyakhetarpal Feb 21, 2024
9941e05
Try fixing bad file descriptors via `os.path.abspath`
agriyakhetarpal Feb 21, 2024
7f95d65
Revisit NumPy-style `IS_WASM` check
agriyakhetarpal Feb 21, 2024
cd69215
Fix `os` module kwargs error
agriyakhetarpal Feb 21, 2024
0249892
Revert "Fix `os` module kwargs error"
agriyakhetarpal Feb 21, 2024
f20d088
Revert "Try fixing bad file descriptors via `os.path.abspath`"
agriyakhetarpal Feb 21, 2024
90d13d0
Hope to fix a few `mra_roundtrip` data tests
agriyakhetarpal Feb 21, 2024
15b6eae
Try loading data files via conversion to abspath
agriyakhetarpal Feb 21, 2024
bc67ce7
Some more path-abspath trickery
agriyakhetarpal Feb 21, 2024
3b743b5
TST: fix test condition for Pyodide
rgommers Feb 21, 2024
f2d8a9c
TST: ensure file handles get closed
rgommers Feb 21, 2024
d5008f7
use importlib.resources instead of `__file__`
rgommers Feb 21, 2024
a4ab51e
try caching data loading
rgommers Feb 21, 2024
f69782e
Git-ignore pytest cache directory tree
agriyakhetarpal Feb 22, 2024
2e0e490
Remove incorrect boolean statement provided to `except` clause
agriyakhetarpal Feb 22, 2024
ef078b0
Rewrite `.npy` file paths as `.npz` archives
agriyakhetarpal Feb 22, 2024
7633198
Convert ecg to NumPy compressed archive
agriyakhetarpal Feb 22, 2024
b031e4a
Convert sst_nino3 to NumPy compressed archive
agriyakhetarpal Feb 22, 2024
ae1d470
Properly skip tests for WASM and wherever threading isn't available
agriyakhetarpal Feb 22, 2024
fa3a852
Clean up conditions to be met to skip tests
agriyakhetarpal Feb 22, 2024
0343b6d
Use better job name to reflect Pyodide build
agriyakhetarpal Feb 22, 2024
dedac16
Remove workflow run contexts, i.e., run on pushes and PRs
agriyakhetarpal Feb 22, 2024
7315b43
import `importlib.resources`, mark `/pywt/data/` as constant
agriyakhetarpal Feb 22, 2024
4a962c9
Remove unneeded `@uses_futures` decorator
agriyakhetarpal Feb 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/emscripten.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Test Pyodide build for PyWavelets

on:
push:
branches:
- master
- v1.**
pull_request:
branches:
- master
- v1.**

env:
FORCE_COLOR: 3

jobs:
build_wasm_emscripten:
name: Build PyWavelets for Pyodide
runs-on: ubuntu-latest
# Uncomment the following line to test changes on a fork
# if: github.repository == 'PyWavelets/pywt'
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python 3.11
id: setup-python
uses: actions/setup-python@v2
with:
python-version: '3.11.2'

- name: Install prerequisites
run: |
python -m pip install pyodide-build "pydantic<2"
echo EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version) >> $GITHUB_ENV

- name: Set up Emscripten toolchain
uses: mymindstorm/setup-emscripten@v14
with:
version: ${{ env.EMSCRIPTEN_VERSION }}
actions-cache-folder: emsdk-cache

- name: Set up Node.js
uses: actions/[email protected]
with:
node-version: '18'

- name: Build PyWavelets
run: |
pyodide build

- name: Install and test wheel
run: |
pyodide venv .venv-pyodide
source .venv-pyodide/bin/activate
pip install dist/*.whl
pushd demo
pip install matplotlib pytest
python -c "import pywt; print(pywt.__version__)"
pytest --pyargs pywt
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ __pycache__
*.py[co]
*.pyd
*.so
.DS_Store
.pytest_cache/

# Packages
*.egg
Expand Down Expand Up @@ -32,6 +34,12 @@ cythonize.dat
pywt/version.py
build.log

# Virtual environments
.env/
env/
venv/
.venv/

# asv files
asv/env
asv/html
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ For more usage examples see the `demo`_ directory in the source package.
Installation
------------

PyWavelets supports `Python`_ >=3.7, and is only dependent on `NumPy`_
PyWavelets supports `Python`_ >=3.9, and is only dependent on `NumPy`_
(supported versions are currently ``>= 1.14.6``). To pass all of the tests,
`Matplotlib`_ is also required. `SciPy`_ is also an optional dependency. When
present, FFT-based continuous wavelet transforms will use FFTs from SciPy
Expand Down
3 changes: 1 addition & 2 deletions demo/batch_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
from concurrent import futures
except ImportError:
raise ImportError(
"This demo requires concurrent.futures. It can be installed for "
"for python 2.x via: pip install futures")
"This demo requires concurrent.futures. If you are on WebAssembly, this is not available.")

import numpy as np
from numpy.testing import assert_array_equal
Expand Down
16 changes: 11 additions & 5 deletions pywt/_pytest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""common test-related code."""
import os
import sys
import platform
import multiprocessing
import numpy as np
import pytest
Expand All @@ -18,15 +19,18 @@
]

try:
if sys.version_info[0] == 2:
import futures
else:
from concurrent import futures
from concurrent import futures
max_workers = multiprocessing.cpu_count()
futures_available = True
except ImportError:
futures_available = False
futures = None
max_workers = 1

# Check if running on Emscripten/WASM, and skip tests that require concurrency.
# Relevant issue: https://github.com/pyodide/pyodide/issues/237
IS_WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"])


# check if pymatbridge + MATLAB tests should be run
matlab_result_dict_dwt = None
Expand Down Expand Up @@ -57,7 +61,9 @@
matlab_result_dict_dwt = np.load(matlab_data_file_dwt)

uses_futures = pytest.mark.skipif(
not futures_available, reason='futures not available')
not futures_available or IS_WASM,
reason='futures is not available, or running via Pyodide/WASM.')
# not futures_available, reason='futures not available')
uses_matlab = pytest.mark.skipif(
matlab_missing, reason='pymatbridge and/or Matlab not available')
uses_pymatbridge = pytest.mark.skipif(
Expand Down
35 changes: 25 additions & 10 deletions pywt/data/_readers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import functools
import importlib.resources
import os

import numpy as np


_DATADIR = importlib.resources.files('pywt.data')


@functools.cache
def ascent():
"""
Get an 8-bit grayscale bit-depth, 512 x 512 derived image for
Expand Down Expand Up @@ -36,11 +42,13 @@ def ascent():
>>> plt.show() # doctest: +SKIP

"""
fname = os.path.join(os.path.dirname(__file__), 'ascent.npz')
ascent = np.load(fname)['data']
with importlib.resources.as_file(_DATADIR.joinpath('ascent.npz')) as f:
ascent = np.load(f)['data']

return ascent


@functools.cache
def aero():
"""
Get an 8-bit grayscale bit-depth, 512 x 512 derived image for
Expand Down Expand Up @@ -71,11 +79,13 @@ def aero():
>>> plt.show() # doctest: +SKIP

"""
fname = os.path.join(os.path.dirname(__file__), 'aero.npz')
aero = np.load(fname)['data']
with importlib.resources.as_file(_DATADIR.joinpath('aero.npz')) as f:
aero = np.load(f)['data']

return aero


@functools.cache
def camera():
"""
Get an 8-bit grayscale bit-depth, 512 x 512 derived image for
Expand Down Expand Up @@ -117,11 +127,13 @@ def camera():
>>> plt.show() # doctest: +SKIP

"""
fname = os.path.join(os.path.dirname(__file__), 'camera.npz')
camera = np.load(fname)['data']
with importlib.resources.as_file(_DATADIR.joinpath('camera.npz')) as f:
camera = np.load(f)['data']

return camera


@functools.cache
def ecg():
"""
Get 1024 points of an ECG timeseries.
Expand All @@ -147,11 +159,13 @@ def ecg():
[<matplotlib.lines.Line2D object at ...>]
>>> plt.show() # doctest: +SKIP
"""
fname = os.path.join(os.path.dirname(__file__), 'ecg.npy')
ecg = np.load(fname)
with importlib.resources.as_file(_DATADIR.joinpath('ecg.npz')) as f:
ecg = np.load(f)['data']

return ecg


@functools.cache
def nino():
"""
This data contains the averaged monthly sea surface temperature in degrees
Expand Down Expand Up @@ -183,8 +197,9 @@ def nino():
[<matplotlib.lines.Line2D object at ...>]
>>> plt.show() # doctest: +SKIP
"""
fname = os.path.join(os.path.dirname(__file__), 'sst_nino3.npy')
sst_csv = np.load(fname)
with importlib.resources.as_file(_DATADIR.joinpath('sst_nino3.npz')) as f:
sst_csv = np.load(f)['data']

# sst_csv = pd.read_csv("http://www.cpc.ncep.noaa.gov/data/indices/ersst4.nino.mth.81-10.ascii", sep=' ', skipinitialspace=True)
# take only full years
n = int(np.floor(sst_csv.shape[0]/12.)*12.)
Expand Down
Binary file renamed pywt/data/ecg.npy → pywt/data/ecg.npz
Binary file not shown.
Binary file renamed pywt/data/sst_nino3.npy → pywt/data/sst_nino3.npz
Binary file not shown.
4 changes: 3 additions & 1 deletion pywt/tests/test_concurrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
import warnings
import numpy as np
from functools import partial

import pytest
from numpy.testing import assert_array_equal, assert_allclose
from pywt._pytest import uses_futures, futures, max_workers

from pywt._pytest import uses_futures, futures, max_workers
import pywt


Expand Down
2 changes: 2 additions & 0 deletions pywt/tests/test_mra.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pywt
from pywt import data
from pywt._pytest import uses_futures

# tolerances used in accuracy comparisons
tol_single = 1e-6
Expand All @@ -17,6 +18,7 @@
# 1d mra tests
####

@uses_futures
@pytest.mark.parametrize('wavelet', ['db2', 'sym4', 'coif5'])
@pytest.mark.parametrize('transform', ['dwt', 'swt'])
@pytest.mark.parametrize('mode', pywt.Modes.modes)
Expand Down
Loading