Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Add wasm CI#29093
Uh oh!
There was an error while loading.Please reload this page.
Conversation
The last commit is a hack, and I don't like that it's necessary. To avoid the This also does not fix anything in browsers; I'm unable to run any code that requires FreeType with the same exception. |
I've figured out what these are:
And this one doesn't seem to have failed on CI, so it does seem to be something to do with memory allocation patterns, and not something inherent to this test. |
agriyakhetarpal commentedNov 6, 2024
Thanks for this,@QuLogic! Some quick comments for now on the "2 mlab testsdon't raise This is generally because of the lack of observability for floating-point exceptions in the WASM runtime, so these tests will have to be skipped for now. Similar issues have been noted here:pyodide/pyodide#4859 and in previous conversations, too.
While support for PyPI is going to demand a PEP and some effort across packaging tooling, in the meantime, it would be great for Matplotlib to publish these WASM wheels when ready to thehttps://anaconda.org/scientific-python-nightly-wheels index. We're uploading them for NumPy, |
agriyakhetarpal commentedNov 6, 2024
xrefpyodide/pyodide#4510 because this effort will be helpful there, too. |
OK, I will skip them, then.
Unfortunately, I don't see anything obvious there; they appear to be changing their extension itself, but not anything on the FreeType side of the build.
We can publish there; it's just that these include the test images and are 5 times bigger as a result. |
agriyakhetarpal commentedNov 6, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I see. In thein-tree recipe, we provide the test images in a separate That is, we could use the standard P.S. this is under the assumption that the images listed under |
QuLogic commentedNov 6, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
So this was unrelated now. The problem was in the debugger I had "Pause on caught exceptions" enabled, which for some reason made it crash. Now, with the following page (note I symlinked the wheel name to a generic one so I didn't need to edit the page repeatedly): Example HTML page<!DOCTYPE html><html><head><scriptsrc="https://cdn.jsdelivr.net/pyodide/v0.26.3/full/pyodide.js"></script></head><body><h1>Pyodide test page</h1> Open your browser console to see Pyodide output<br/><buttonid="run"onclick="evaluatePython()">Run</button><h2>Output:</h2><textareaid="output"style="width: 50%;"rows="100"disabled></textarea><canvasid="canvas"style="vertical-align: top;"></canvas><scripttype="text/javascript">construn=document.getElementById("run");constoutput=document.getElementById("output");constcanvas=document.getElementById("canvas");output.value="Initializing... ";run.disabled=true;asyncfunctionmain(){letpyodide=awaitloadPyodide();pyodide.setDebug(true);pyodide.setStdout({batched:(s)=>output.value+=`${s}\n`,});pyodide.setStderr({batched:(s)=>output.value+=`[ERR]${s}\n`,});awaitpyodide.loadPackage("micropip");letmicropip=pyodide.pyimport("micropip");awaitmicropip.install("http://127.0.0.1:8000/matplotlib-3.10.0.dev0-cp312-cp312-pyodide_2024_0_wasm32.whl");output.value+="Done!\n";run.disabled=false;returnpyodide;}letpyodideReadyPromise=main();asyncfunctionrunStuff(code){letpyodide=awaitpyodideReadyPromise;output.value+=`>>>${code}\n`;try{returnpyodide.runPython(code);}catch(err){output.value+=`[EXC]:${err}`;}}asyncfunctionevaluatePython(){awaitrunStuff(` import sys print(sys.version) `);awaitrunStuff(` import matplotlib print(matplotlib.__version__) `);constfig=awaitrunStuff(` import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x * np.pi) fig, ax = plt.subplots() fig.text(0.5, 0.5, 'Test', fontsize=24, horizontalalignment='center', verticalalignment='center') ax.plot(x, y) print(fig) fig `);fig.canvas.draw();constwidth=fig.bbox.width;constheight=fig.bbox.height;constbuffer=fig.canvas.buffer_rgba();constdata=buffer.getBuffer("u8clamped");constimageData=newImageData(data.data,width,height);canvas.width=width;canvas.height=height;constctx=canvas.getContext("2d");ctx.putImageData(imageData,0,0);data.release();}</script></body></html> It's probably not the most efficient implementation, but it's enough to prove the wasm wheel is working. |
I'm surprised to hear this, what's the best way to reproduce the failure? |
Thanks so much@QuLogic for working on this! |
CFLAGS = "-fexceptions" | ||
CXXFLAGS = "-fexceptions" | ||
LDFLAGS = "-fexceptions" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Someone was suggesting recently that we ought to make this the default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yes, Henry was suggesting it in our Discord server, IIRC, through whichpybind/pybind11#5298 came up (as linked in the code comment above)
Running this myself I'm getting a crash in
|
QuLogic commentedNov 7, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Looking closer, these two tests use >>>importos>>>fh=open(os.devnull,'w')>>>fh.tell()Traceback (mostrecentcalllast):File"<stdin>",line1,in<module>io.UnsupportedOperation:underlyingstreamisnotseekable I pushed a change to avoid using |
Are you building with |
hoodmane commentedNov 8, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'm not building with cibuildwheel because there are test failures and I don't know how to get a copy of the wheel to inspect/debug if tests fail.@henryiii is there an easy way to do this? Anyways I did copy the exact build command out of the log and copied the environment changes. So I think I'm building it right.
I bet this is the problem. |
You can skip the tests by building with:
|
More of these are happening now that the leaky file handle was fixed. I've opened#29102 to minimize memory usage in the image comparisons, and that should fix these. |
I've come back to this and think it's in a reasonable spot now. With a bit of modification of the testing procedure, the workflow is able to build normal wheels that don't include baseline images, so I've modified the nightly upload to publish the wasm wheels to the nightly index. There are 2 things that may be workarounds for pyodide/emscripten/wasm issues:
|
QuLogic commentedMay 17, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Further testing shows that I don't need the first thing. Perhaps something improved in the toolchain somewhere since last time. FreeType still needs all symbols or else everything using text crashes. |
This was originally for i686 on Fedora, but is now applicable to WASM,which is 32-bit. The older implementation doesn't OOM.
This checks `os.geteuid`, but this is only available on Unix, notEmscripten or WASM.
In Enscripten/WASM, this module can be imported, but doesn't work, so wecan't fall back to `dummy_threading` at import-time as we used to do.
The file system is either sandboxed or there are simply no other fonts,so limit ourselves to our pre-shipped fonts.
On WASM, which is wholly 32-bit, casting unsigned int to signed long isa narrowing conversion, which it seems to treat as an error. The Aggbuffer cannot be over `(1<<23)` in width or height, so this cast issafe even with the smaller sizes.
This adds a `pyproject.toml` config for it, so you can replicate locallywith cibuildwheel.
On wasm, this file doesn't support seeking, which is sometimes necessarydepending on file type.
|
assert_allclose(leg.get_window_extent().extents, | ||
legbb[nn]) | ||
assert_allclose(axs.get_window_extent().extents, axbb[nn], | ||
rtol=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
rtol of 1 seems quite loose here. Is there something more exact that we could check?
@@ -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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Update the reason text to something like "chmod() doesn't work on this platform"
Uh oh!
There was an error while loading.Please reload this page.
PR summary
This adds wasm builds on CI through
cibuildwheel
; these are geared towards testing only, and not publishing, as they usecibuildwheel
's test command, which requires shipping the test result images (which we don't do for release wheels.)The wasm platform has several constraints:
threading
(MNT: Remove dummy_threading because threading is always available #23073), it's not actually supportedThreading support pyodide/pyodide#237 and we need to either ignore it, or skip tests that use it.shutil.which
returns a real path, but due to the below, fails to run, so we need to explicitly catch that.mlab.stride_windows
implementation, as the NumPystride_tricks
implementation OOMs. This is now inUse old stride_windows implementation on 32-bit builds #29115 for separate review.This gets us to about 80% tests passing, 19% skipped (mostly the SVG/PDF, I think), and a small handful that fail:
file.seek
/file.tell
? That seems an odd limitation, especially for the latter. -> patched here to avoidos.devnull
mlab
testsdon't raisenumpy.linalg.LinAlgError
; haven't investigated at all. ->@agriyakhetarpal says to skip these, so I have.tests/test_simplification.py::test_throw_rendering_complexity_exceeded
throwsMemoryError
instead ofOverflowError
; probably this will have to be skipped like the OOM ones, but I haven't checked the implementation.tests/test_skew.py::test_skew_rectangle[png]
fails to allocate the (8, 8)-inch figure, but there are several tests with a larger figure, so I'm not sure why this one in particular fails. -> I've openedTST: Calculate RMS and diff image in C++ #29102 to optimize the image comparisons, which will fix these.cc@agriyakhetarpal
Closes#27870
PR checklist