From e804200d4141d09d103c4e3eab263a99bebc62d7 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 4 Jun 2024 16:45:34 -0700 Subject: [PATCH 01/12] TST: Fix condition for test_tmpconfigdir_warning This checks `os.geteuid`, but this is only available on Unix, not Emscripten or WASM. --- lib/matplotlib/tests/test_matplotlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py index 37b41fafdb78..3b55b526dbe0 100644 --- a/lib/matplotlib/tests/test_matplotlib.py +++ b/lib/matplotlib/tests/test_matplotlib.py @@ -18,9 +18,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", +@pytest.mark.skipif(sys.platform not in ["linux", "darwin"], reason="chmod() doesn't work as is on Windows") -@pytest.mark.skipif(sys.platform != "win32" and os.geteuid() == 0, +@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.""" From ee446a02f81c3588a3377496e395336ac4aa0c34 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 4 Jun 2024 17:15:43 -0700 Subject: [PATCH 02/12] Ignore threading errors when preparing font cache In Enscripten/WASM, this module can be imported, but doesn't work, so we can't fall back to `dummy_threading` at import-time as we used to do. --- lib/matplotlib/font_manager.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 9aa8dccde444..f922f13e5e78 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1092,9 +1092,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), @@ -1107,7 +1110,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): """ From 522c318c8f5ce4bd7e8b695ad0707592c120d691 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 4 Jun 2024 17:28:28 -0700 Subject: [PATCH 03/12] Don't attempt to load system fonts on Emscripten The file system is either sandboxed or there are simply no other fonts, so limit ourselves to our pre-shipped fonts. --- lib/matplotlib/font_manager.py | 3 +++ lib/matplotlib/tests/test_font_manager.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index f922f13e5e78..cac7ea4eba1b 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -287,6 +287,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': diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index d15b892b3eea..d9c03c8465af 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -20,7 +20,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(): From 94c0924edfe652363bceb7bdcc5e6812aeadbf06 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 26 Sep 2024 06:14:03 -0400 Subject: [PATCH 04/12] ft2font: Explicitly cast dimensions when creating Python buffer On WASM, which is wholly 32-bit, casting unsigned int to signed long is a narrowing conversion, which it seems to treat as an error. The Agg buffer cannot be over `(1<<23)` in width or height, so this cast is safe even with the smaller sizes. --- src/_backend_agg_wrapper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 269e2aaa9ee5..2bad7f4d0b9f 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -250,12 +250,12 @@ PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used()) .def_buffer([](RendererAgg *renderer) -> py::buffer_info { std::vector shape { - renderer->get_height(), - renderer->get_width(), + static_cast(renderer->get_height()), + static_cast(renderer->get_width()), 4 }; std::vector strides { - renderer->get_width() * 4, + static_cast(renderer->get_width() * 4), 4, 1 }; From f2a3abc8a1decf1ca5dffb3d6dcf149c76092e53 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 5 Nov 2024 04:33:59 -0500 Subject: [PATCH 05/12] wasm: Skip more subprocess & thread tests --- lib/matplotlib/animation.py | 2 ++ lib/matplotlib/dviread.py | 4 ++-- lib/matplotlib/testing/__init__.py | 5 ++++- lib/matplotlib/testing/decorators.py | 2 ++ lib/matplotlib/tests/test_animation.py | 4 ++++ lib/matplotlib/tests/test_dviread.py | 3 ++- lib/matplotlib/tests/test_figure.py | 5 +++++ lib/matplotlib/tests/test_font_manager.py | 2 ++ lib/matplotlib/texmanager.py | 8 ++++---- 9 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index a0236a6956cb..1fc39560801b 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -360,6 +360,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 diff --git a/lib/matplotlib/dviread.py b/lib/matplotlib/dviread.py index bd21367ce73d..6d6d66e56944 100644 --- a/lib/matplotlib/dviread.py +++ b/lib/matplotlib/dviread.py @@ -1070,7 +1070,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: @@ -1090,7 +1090,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: diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 19113d399626..4731848aa6b1 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -88,6 +88,9 @@ def subprocess_run_for_testing(command, env=None, timeout=60, stdout=None, 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: @@ -175,7 +178,7 @@ def _has_tex_package(package): try: mpl.dviread.find_tex_file(f"{package}.sty") return True - except FileNotFoundError: + except (FileNotFoundError, OSError): return False diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 6f1af7debdb3..82900fa7d4e8 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -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) diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py index d026dae59533..a47c92ed648a 100644 --- a/lib/matplotlib/tests/test_animation.py +++ b/lib/matplotlib/tests/test_animation.py @@ -278,6 +278,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" @@ -546,6 +548,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): diff --git a/lib/matplotlib/tests/test_dviread.py b/lib/matplotlib/tests/test_dviread.py index 7b7ff151be18..2aa3abe12e4b 100644 --- a/lib/matplotlib/tests/test_dviread.py +++ b/lib/matplotlib/tests/test_dviread.py @@ -1,6 +1,7 @@ import json from pathlib import Path import shutil +import sys import matplotlib.dviread as dr import pytest @@ -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' diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index edf5ea05f119..d71dca15e7c3 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -3,6 +3,7 @@ import io import pickle import platform +import sys from threading import Timer from types import SimpleNamespace import warnings @@ -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() @@ -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() diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index d9c03c8465af..21e1c16075fa 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -228,6 +228,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(): diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index a374bfba8cab..85417be32fb0 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -247,10 +247,6 @@ def _run_checked_subprocess(cls, command, tex, *, cwd=None): report = subprocess.check_output( command, cwd=cwd if cwd is not None else cls._texcache, stderr=subprocess.STDOUT) - except FileNotFoundError as exc: - raise RuntimeError( - f'Failed to process string with tex because {command[0]} ' - 'could not be found') from exc except subprocess.CalledProcessError as exc: raise RuntimeError( '{prog} was not able to process the following string:\n' @@ -263,6 +259,10 @@ def _run_checked_subprocess(cls, command, tex, *, cwd=None): tex=tex.encode('unicode_escape'), exc=exc.output.decode('utf-8', 'backslashreplace')) ) from None + except (FileNotFoundError, OSError) as exc: + raise RuntimeError( + f'Failed to process string with tex because {command[0]} ' + 'could not be found') from exc _log.debug(report) return report From 2e4450c63d27787cbb30e2a84c676763fb2294cd Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 22 Aug 2022 18:43:28 -0400 Subject: [PATCH 06/12] Use old stride_windows implementation on 32-bit builds This was originally for i686 on Fedora, but is now applicable to WASM, which is 32-bit. The older implementation doesn't OOM. --- lib/matplotlib/mlab.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index 8326ac186e31..bdaa34b73427 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -49,6 +49,7 @@ import functools from numbers import Number +import sys import numpy as np @@ -210,6 +211,30 @@ def detrend_linear(y): return y - (b*x + a) +def _stride_windows(x, n, noverlap=0): + if noverlap >= n: + raise ValueError('noverlap must be less than n') + if n < 1: + raise ValueError('n cannot be less than 1') + + x = np.asarray(x) + + if n == 1 and noverlap == 0: + return x[np.newaxis] + if n > x.size: + raise ValueError('n cannot be greater than the length of x') + + # np.lib.stride_tricks.as_strided easily leads to memory corruption for + # non integer shape and strides, i.e. noverlap or n. See #3845. + noverlap = int(noverlap) + n = int(n) + + step = n - noverlap + shape = (n, (x.shape[-1]-noverlap)//step) + strides = (x.strides[0], step*x.strides[0]) + return np.lib.stride_tricks.as_strided(x, shape=shape, strides=strides) + + def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): @@ -304,8 +329,11 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, raise ValueError( "The window length must match the data's first dimension") - result = np.lib.stride_tricks.sliding_window_view( - x, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: # NumPy version on 32-bit OOMs. + result = np.lib.stride_tricks.sliding_window_view( + x, NFFT, axis=0)[::NFFT - noverlap].T + else: + result = _stride_windows(x, NFFT, noverlap=noverlap) result = detrend(result, detrend_func, axis=0) result = result * window.reshape((-1, 1)) result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] @@ -313,8 +341,11 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, if not same_data: # if same_data is False, mode must be 'psd' - resultY = np.lib.stride_tricks.sliding_window_view( - y, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: # NumPy version on 32-bit OOMs. + resultY = np.lib.stride_tricks.sliding_window_view( + y, NFFT, axis=0)[::NFFT - noverlap].T + else: + resultY = _stride_windows(y, NFFT, noverlap=noverlap) resultY = detrend(resultY, detrend_func, axis=0) resultY = resultY * window.reshape((-1, 1)) resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] From 2f92b80bd11a718a090e176b13fdc1f867b83a16 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 5 Nov 2024 06:14:35 -0500 Subject: [PATCH 07/12] Skip tests that OOM on WASM --- lib/matplotlib/tests/test_agg.py | 2 ++ lib/matplotlib/tests/test_image.py | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index 59387793605a..2a2a97f074aa 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -1,4 +1,5 @@ import io +import sys import numpy as np from numpy.testing import assert_array_almost_equal @@ -279,6 +280,7 @@ def test_draw_path_collection_error_handling(): fig.canvas.draw() +@pytest.mark.skipif(sys.platform == 'emscripten', reason='Too large for emscripten VM') def test_chunksize_fails(): # NOTE: This test covers multiple independent test scenarios in a single # function, because each scenario uses ~2GB of memory and we don't diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 24a0ab929bbf..f82aad388f6d 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -1507,6 +1507,7 @@ def test_rc_interpolation_stage(): mpl.rcParams["image.interpolation_stage"] = val +@pytest.mark.skipif(sys.platform == 'emscripten', reason='Figure too large for WASM') # We check for the warning with a draw() in the test, but we also need to # filter the warning as it is emitted by the figure test decorator @pytest.mark.filterwarnings(r'ignore:Data with more than .* ' From 9099e9e788137669bde138cfcd94134ca6547a85 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 6 Nov 2024 05:20:05 -0500 Subject: [PATCH 08/12] Skip tests that expect LinAlgError on WASM --- lib/matplotlib/tests/test_mlab.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index 3b0d2529b5f1..3496027bb2f6 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -1,3 +1,5 @@ +import sys + from numpy.testing import (assert_allclose, assert_almost_equal, assert_array_equal, assert_array_almost_equal_nulp) import numpy as np @@ -873,6 +875,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]]) @@ -886,6 +890,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]]) From 7a30cf3fe874d410fe759424b4f46a6e9b57bc65 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 6 Nov 2024 00:25:44 -0500 Subject: [PATCH 09/12] ci: Add a build for wasm This adds a `pyproject.toml` config for it, so you can replicate locally with cibuildwheel. --- .github/workflows/wasm.yml | 58 ++++++++++++++++++++++++++++++++++++++ pyproject.toml | 14 +++++++++ 2 files changed, 72 insertions(+) create mode 100644 .github/workflows/wasm.yml diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 000000000000..dc854a353588 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -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@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + name: Install Python + with: + python-version: '3.12' + + - name: Build wheels for wasm + uses: pypa/cibuildwheel@7940a4c0e76eb2030e473a5f864f291f63ee879b # v2.21.3 + env: + CIBW_PLATFORM: "pyodide" + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ runner.os }}-${{ matrix.cibw_archs }} + path: ./wheelhouse/*.whl + if-no-files-found: error diff --git a/pyproject.toml b/pyproject.toml index 48a174731440..0771404fbf10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -318,3 +318,17 @@ testpaths = ["lib"] addopts = [ "--import-mode=importlib", ] + +# This build is just for testing right now. +[tool.cibuildwheel.pyodide] +build-frontend = "build; args: --exports whole_archive" +test-requires = "pytest" +test-command = "pytest -p no:cacheprovider --pyargs matplotlib" +[tool.cibuildwheel.pyodide.config-settings] +install-args = "--tags=data,python-runtime,runtime,tests" +[tool.cibuildwheel.pyodide.environment] +# Exceptions are needed for pybind11: +# https://github.com/pybind/pybind11/pull/5298 +CFLAGS = "-fexceptions" +CXXFLAGS = "-fexceptions" +LDFLAGS = "-fexceptions" From f1b6047c54681f4768b2aae0f2e631a9c7aabd95 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 6 Nov 2024 01:20:52 -0500 Subject: [PATCH 10/12] Open symbol visibility for FreeType on wasm --- subprojects/packagefiles/freetype-2.6.1-meson/meson.build | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/meson.build b/subprojects/packagefiles/freetype-2.6.1-meson/meson.build index 9a5180ef7586..1fd4bc44e7b5 100644 --- a/subprojects/packagefiles/freetype-2.6.1-meson/meson.build +++ b/subprojects/packagefiles/freetype-2.6.1-meson/meson.build @@ -179,11 +179,17 @@ ft_config_headers += [configure_file(input: 'include/freetype/config/ftoption.h. output: 'ftoption.h', configuration: conf)] +if cc.get_id() == 'emscripten' + kwargs = {} +else + kwargs = {'gnu_symbol_visibility': 'inlineshidden'} +endif + libfreetype = static_library('freetype', base_sources, include_directories: incbase, dependencies: deps, c_args: c_args, - gnu_symbol_visibility: 'inlineshidden', + kwargs: kwargs ) freetype_dep = declare_dependency( From 52579fa157b2539ec3647fe5dc9dd92bba598609 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 7 Nov 2024 18:53:55 -0500 Subject: [PATCH 11/12] TST: Avoid using os.devnull for path tests On wasm, this file doesn't support seeking, which is sometimes necessary depending on file type. --- lib/matplotlib/tests/test_backend_pdf.py | 5 ++--- lib/matplotlib/tests/test_image.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index c816c4715ae2..f220a70788a6 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -1,7 +1,6 @@ import datetime import decimal import io -import os from pathlib import Path import numpy as np @@ -307,8 +306,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()) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index f82aad388f6d..6135bb7dd167 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -193,8 +193,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(): From 98a8b8c07094dd3b21ac4907ce0255b3fe4f4c65 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 7 Nov 2024 19:23:36 -0500 Subject: [PATCH 12/12] Add wasm build to cibuildwheel label --- .github/labeler.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 75adfed57f43..77b79146b47f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -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']