Skip to content

Add wasm CI #29093

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
"CI: Run cibuildwheel":
- changed-files:
- any-glob-to-any-file: ['.github/workflows/cibuildwheel.yml']
- any-glob-to-any-file:
- '.github/workflows/cibuildwheel.yml'
- '.github/workflows/wasm.yml'
"CI: Run cygwin":
- changed-files:
- any-glob-to-any-file: ['.github/workflows/cygwin.yml']
Expand Down
40 changes: 20 additions & 20 deletions .github/workflows/nightlies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ jobs:
run: |
PROJECT_REPO="matplotlib/matplotlib"
BRANCH="main"
WORKFLOW_NAME="cibuildwheel.yml"
ARTIFACT_PATTERN="cibw-wheels-*"

gh run --repo "${PROJECT_REPO}" \
list --branch "${BRANCH}" \
--workflow "${WORKFLOW_NAME}" \
--json event,status,conclusion,databaseId > runs.json
RUN_ID=$(
jq --compact-output \
'[
.[] |
# Filter on "push" events to main (merged PRs) ...
select(.event == "push") |
# that have completed successfully ...
select(.status == "completed" and .conclusion == "success")
] |
# and get ID of latest build of wheels.
sort_by(.databaseId) | reverse | .[0].databaseId' runs.json
)
gh run --repo "${PROJECT_REPO}" view "${RUN_ID}"
gh run --repo "${PROJECT_REPO}" \
download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}"
for WORKFLOW_NAME in cibuildwheel.yml wasm.yml; do
gh run --repo "${PROJECT_REPO}" \
list --branch "${BRANCH}" \
--workflow "${WORKFLOW_NAME}" \
--json event,status,conclusion,databaseId > runs.json
RUN_ID=$(
jq --compact-output \
'[
.[] |
# Filter on "push" events to main (merged PRs) ...
select(.event == "push") |
# that have completed successfully ...
select(.status == "completed" and .conclusion == "success")
] |
# and get ID of latest build of wheels.
sort_by(.databaseId) | reverse | .[0].databaseId' runs.json
)
gh run --repo "${PROJECT_REPO}" view "${RUN_ID}"
gh run --repo "${PROJECT_REPO}" download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}"
done

mkdir dist
mv ${ARTIFACT_PATTERN}/*.whl dist/
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
name: Build wasm wheels

on:
# Save CI by only running this on release branches or tags.
push:
branches:
- main
- v[0-9]+.[0-9]+.x
tags:
- v*
# Also allow running this action on PRs if requested by applying the
# "Run cibuildwheel" label.
pull_request:
types:
- opened
- synchronize
- reopened
- labeled

permissions:
contents: read

jobs:
build_wasm:
if: >-
github.event_name == 'push' ||
github.event_name == 'pull_request' && (
(
github.event.action == 'labeled' &&
github.event.label.name == 'CI: Run cibuildwheel'
) ||
contains(github.event.pull_request.labels.*.name,
'CI: Run cibuildwheel')
)
name: Build wasm
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
name: Install Python
with:
python-version: '3.13'

- name: Build wheels for wasm
uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a # v2.23.3
env:
CIBW_PLATFORM: "pyodide"

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: cibw-wheels-wasm
path: ./wheelhouse/*.whl
if-no-files-found: error
2 changes: 2 additions & 0 deletions lib/matplotlib/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ def bin_path(cls):
@classmethod
def isAvailable(cls):
"""Return whether a MovieWriter subclass is actually available."""
if sys.platform == 'emscripten':
return False
return shutil.which(cls.bin_path()) is not None


Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ def find_tex_file(filename):

try:
lk = _LuatexKpsewhich()
except FileNotFoundError:
except (FileNotFoundError, OSError):
lk = None # Fallback to directly calling kpsewhich, as below.

if lk:
Expand All @@ -1155,7 +1155,7 @@ def find_tex_file(filename):
path = (cbook._check_and_log_subprocess(['kpsewhich', filename],
_log, **kwargs)
.rstrip('\n'))
except (FileNotFoundError, RuntimeError):
except (FileNotFoundError, OSError, RuntimeError):
path = None

if path:
Expand Down
15 changes: 11 additions & 4 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
if sys.platform == 'win32':
installed_fonts = _get_win32_installed_fonts()
fontpaths = []
elif sys.platform == 'emscripten':
installed_fonts = []
fontpaths = []
else:
installed_fonts = _get_fontconfig_fonts()
if sys.platform == 'darwin':
Expand Down Expand Up @@ -1090,9 +1093,12 @@ def __init__(self, size=None, weight='normal'):
self.ttflist = []

# Delay the warning by 5s.
timer = threading.Timer(5, lambda: _log.warning(
'Matplotlib is building the font cache; this may take a moment.'))
timer.start()
try:
timer = threading.Timer(5, lambda: _log.warning(
'Matplotlib is building the font cache; this may take a moment.'))
timer.start()
except RuntimeError:
timer = None
try:
for fontext in ["afm", "ttf"]:
for path in [*findSystemFonts(paths, fontext=fontext),
Expand All @@ -1105,7 +1111,8 @@ def __init__(self, size=None, weight='normal'):
_log.info("Failed to extract font properties from %s: "
"%s", path, exc)
finally:
timer.cancel()
if timer:
timer.cancel()

def addfont(self, path):
"""
Expand Down
7 changes: 6 additions & 1 deletion lib/matplotlib/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,14 @@ def subprocess_run_for_testing(command, env=None, timeout=60, stdout=None,

Raises
------
pytest.skip
If running on emscripten, which does not support subprocesses.
pytest.xfail
If platform is Cygwin and subprocess reports a fork() failure.
"""
if sys.platform == 'emscripten':
import pytest
pytest.skip('emscripten does not support subprocesses')
if capture_output:
stdout = stderr = subprocess.PIPE
try:
Expand Down Expand Up @@ -187,7 +192,7 @@ def _has_tex_package(package):
try:
mpl.dviread.find_tex_file(f"{package}.sty")
return True
except FileNotFoundError:
except (FileNotFoundError, OSError):
return False


Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/testing/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ def copy_baseline(self, baseline, extension):
try:
if 'microsoft' in uname().release.lower():
raise OSError # On WSL, symlink breaks silently
if sys.platform == 'emscripten':
raise OSError
os.symlink(orig_expected_path, expected_fname)
except OSError: # On Windows, symlink *may* be unavailable.
shutil.copyfile(orig_expected_path, expected_fname)
Expand Down
4 changes: 4 additions & 0 deletions lib/matplotlib/tests/test_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ def test_no_length_frames(anim):
anim.save('unused.null', writer=NullMovieWriter())


@pytest.mark.skipif(sys.platform == 'emscripten',
reason='emscripten does not support subprocesses')
def test_movie_writer_registry():
assert len(animation.writers._registered) > 0
mpl.rcParams['animation.ffmpeg_path'] = "not_available_ever_xxxx"
Expand Down Expand Up @@ -522,6 +524,8 @@ def test_disable_cache_warning(anim):
def test_movie_writer_invalid_path(anim):
if sys.platform == "win32":
match_str = r"\[WinError 3] .*\\\\foo\\\\bar\\\\aardvark'"
elif sys.platform == "emscripten":
match_str = r"\[Errno 44] .*'/foo"
else:
match_str = r"\[Errno 2] .*'/foo"
with pytest.raises(FileNotFoundError, match=match_str):
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8289,7 +8289,7 @@ def test_normal_axes():
]
for nn, b in enumerate(bbaxis):
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2)
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=1)

target = [
[150.0, 119.999, 930.0, 11.111],
Expand All @@ -8307,7 +8307,7 @@ def test_normal_axes():

target = [85.5138, 75.88888, 1021.11, 1017.11]
targetbb = mtransforms.Bbox.from_bounds(*target)
assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=2)
assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=1)

# test that get_position roundtrips to get_window_extent
axbb = ax.get_position().transformed(fig.transFigure).bounds
Expand Down
5 changes: 2 additions & 3 deletions lib/matplotlib/tests/test_backend_pdf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import datetime
import decimal
import io
import os
from pathlib import Path

import numpy as np
Expand Down Expand Up @@ -291,8 +290,8 @@ def test_text_urls_tex():
assert annot.Rect[1] == decimal.Decimal('0.7') * 72


def test_pdfpages_fspath():
with PdfPages(Path(os.devnull)) as pdf:
def test_pdfpages_fspath(tmp_path):
with PdfPages(tmp_path / 'unused.pdf') as pdf:
pdf.savefig(plt.figure())


Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/tests/test_dviread.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from pathlib import Path
import shutil
import sys

import matplotlib.dviread as dr
import pytest
Expand Down Expand Up @@ -60,7 +61,7 @@ def test_PsfontsMap(monkeypatch):
fontmap[b'%']


@pytest.mark.skipif(shutil.which("kpsewhich") is None,
@pytest.mark.skipif(sys.platform == "emscripten" or shutil.which("kpsewhich") is None,
reason="kpsewhich is not available")
def test_dviread():
dirpath = Path(__file__).parent / 'baseline_images/dviread'
Expand Down
5 changes: 5 additions & 0 deletions lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io
import pickle
import platform
import sys
from threading import Timer
from types import SimpleNamespace
import warnings
Expand Down Expand Up @@ -1605,6 +1606,8 @@ def test_add_axes_kwargs():
plt.close()


@pytest.mark.skipif(sys.platform == 'emscripten',
reason='emscripten does not support threads')
def test_ginput(recwarn): # recwarn undoes warn filters at exit.
warnings.filterwarnings("ignore", "cannot show the figure")
fig, ax = plt.subplots()
Expand All @@ -1627,6 +1630,8 @@ def multi_presses():
np.testing.assert_allclose(fig.ginput(3), [(.3, .4), (.5, .6)])


@pytest.mark.skipif(sys.platform == 'emscripten',
reason='emscripten does not support threads')
def test_waitforbuttonpress(recwarn): # recwarn undoes warn filters at exit.
warnings.filterwarnings("ignore", "cannot show the figure")
fig = plt.figure()
Expand Down
4 changes: 3 additions & 1 deletion lib/matplotlib/tests/test_font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing


has_fclist = shutil.which('fc-list') is not None
has_fclist = sys.platform != 'emscripten' and shutil.which('fc-list') is not None


def test_font_priority():
Expand Down Expand Up @@ -229,6 +229,8 @@ def _model_handler(_):
plt.close()


@pytest.mark.skipif(sys.platform == 'emscripten',
reason='emscripten does not support subprocesses')
@pytest.mark.skipif(not hasattr(os, "register_at_fork"),
reason="Cannot register at_fork handlers")
def test_fork():
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ def test_imsave_rgba_origin(origin):


@pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
def test_imsave_fspath(fmt):
plt.imsave(Path(os.devnull), np.array([[0, 1]]), format=fmt)
def test_imsave_fspath(fmt, tmp_path):
plt.imsave(tmp_path / f'unused.{fmt}', np.array([[0, 1]]), format=fmt)


def test_imsave_color_alpha():
Expand Down
8 changes: 4 additions & 4 deletions lib/matplotlib/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,10 +513,10 @@ def test_figure_legend_outside():
leg = fig.legend(loc='outside ' + todo)
fig.draw_without_rendering()

assert_allclose(axs.get_window_extent().extents,
axbb[nn])
assert_allclose(leg.get_window_extent().extents,
legbb[nn])
assert_allclose(axs.get_window_extent().extents, axbb[nn],
rtol=1e-4)
assert_allclose(leg.get_window_extent().extents, legbb[nn],
rtol=1e-4)


@image_comparison(['legend_stackplot.png'],
Expand Down
6 changes: 3 additions & 3 deletions lib/matplotlib/tests/test_matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ def test_parse_to_version_info(version_str, version_tuple):
assert matplotlib._parse_to_version_info(version_str) == version_tuple


@pytest.mark.skipif(sys.platform == "win32",
reason="chmod() doesn't work as is on Windows")
@pytest.mark.skipif(sys.platform != "win32" and os.geteuid() == 0,
@pytest.mark.skipif(sys.platform not in ["linux", "darwin"],
reason="chmod() doesn't work on this platform")
@pytest.mark.skipif(sys.platform in ["linux", "darwin"] and os.geteuid() == 0,
reason="chmod() doesn't work as root")
def test_tmpconfigdir_warning(tmp_path):
"""Test that a warning is emitted if a temporary configdir must be used."""
Expand Down
4 changes: 4 additions & 0 deletions lib/matplotlib/tests/test_mlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,8 @@ def test_single_dataset_element(self):
with pytest.raises(ValueError):
mlab.GaussianKDE([42])

@pytest.mark.skipif(sys.platform == 'emscripten',
reason="WASM doesn't support floating-point exceptions")
def test_silverman_multidim_dataset(self):
"""Test silverman's for a multi-dimensional array."""
x1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Expand All @@ -897,6 +899,8 @@ def test_silverman_singledim_dataset(self):
y_expected = 0.76770389927475502
assert_almost_equal(mygauss.covariance_factor(), y_expected, 7)

@pytest.mark.skipif(sys.platform == 'emscripten',
reason="WASM doesn't support floating-point exceptions")
def test_scott_multidim_dataset(self):
"""Test scott's output for a multi-dimensional array."""
x1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Expand Down
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy