Add pyodide backend based on webagg - #32148
Conversation
|
|
||
| @classmethod | ||
| def get_javascript(cls, stream=None): | ||
| def get_javascript(cls, stream=None, *, pyodide=False): |
There was a problem hiding this comment.
The extra kwarg here isn't elegant, but it keeps backward compatibility with minimal code changes.
Alternatives would be to reimplement this entirely in backend_pyodide to keep it unchanged here, but that would be quite a lot of code duplication. Or this function could call a number of other shorter functions and backend_pyodide could just override the 2 that it needs to.
| output.write((Path(__file__).parent / "web_backend/js/mpl.js") | ||
| .read_text(encoding="utf-8")) | ||
| if pyodide: | ||
| output.write((Path(__file__).parent / "web_backend/js/mpl_pyodide.js") |
There was a problem hiding this comment.
Here we have already loaded the default mpl.js JavaScript code into the browser page, and then loading mpl_pyodide.js afterwards adds some new code and replaces some of the previous functions.
| } | ||
|
|
||
| if sys.platform == 'emscripten': | ||
| self._BUILTIN_BACKEND_TO_GUI_FRAMEWORK["pyodide"] = "pyodide" |
There was a problem hiding this comment.
Here I am only advertising the existence of the pyodide backend if we are running on emscripten. The alternative would be to always have it present in the list of available backends even when it cannot be used.
There was a problem hiding this comment.
I don't know about how other backends (e.g. wegagg) are advertised, but it makes sense to me as people who try to use pyodide-backend in non-emscripten environment would get runtime error.
|
|
||
| [tool.cibuildwheel.pyodide] | ||
| config-settings.setup-args = [ | ||
| "-DrcParams-backend=pyodide" |
There was a problem hiding this comment.
Here setting pyodide to be the default backend used in pyodide wheels. This is consistent with the current patch in the pyodide matplotlib build. Alternatively we could avoid this and stick with agg as the default backend, and explain to Pyodide users about
import matplotlib as mpl
mpl.use('pyodide')There was a problem hiding this comment.
Yeah, it would be great to make this a default so that users don't need to explicitly set the backend.
|
If this is in core, could it be used as part of the doc builds to make the widgets examples interactive? |
I suppose so, but it would need some infrastructure so that each user only has a single pyodide instance running rather than one per plot. But I think that jupyterlite-sphinx is the go-to project for this, and being JupyterLite-based the python instance runs in a separate thread which gives a better UX. Although at a quick glance I see all the examples produce static plots not interactive, so it would need some checking to see what is currently possible. |
ryanking13
left a comment
There was a problem hiding this comment.
Thanks for upstreaming this!
There is no testing yet. When we have reliable testing of wasm wheels I can add extra tests, similar to the webagg tests, for this.
Yeah, it is slightly tricky to test this as it requires a real browser to test.
I think you can test some part of the features that does not requires DOM (document object) using cibuildwheel by adding
[tool.cibuildwheel.pyodide.environment]
test-command = "python -m pytest <test files>"similar to how scipy is doing. But I guess there will be a lot of tests that need to be skipped.
| } | ||
|
|
||
| if sys.platform == 'emscripten': | ||
| self._BUILTIN_BACKEND_TO_GUI_FRAMEWORK["pyodide"] = "pyodide" |
There was a problem hiding this comment.
I don't know about how other backends (e.g. wegagg) are advertised, but it makes sense to me as people who try to use pyodide-backend in non-emscripten environment would get runtime error.
|
|
||
| [tool.cibuildwheel.pyodide] | ||
| config-settings.setup-args = [ | ||
| "-DrcParams-backend=pyodide" |
There was a problem hiding this comment.
Yeah, it would be great to make this a default so that users don't need to explicitly set the backend.
PR summary
This PR adds an interactive
pyodidebackend to the core Matplotlib code which is based onbackend_webagg_corein a similar way to the existingwebaggandipymplbackends. It has been patched into the Pyodide build of Matplotlib in pyodide-recipes for about a year and a half, but as we are planning to build and upload our own Pyodide wheels to PyPI the backend needs to be part of the core repo here or the functionality will no longer be available. Note that this is for use of Pyodide outside of JupyterLite, as within JupyterLite one would use the interactiveipymplbackend as usual.Because it is not trivial to build and try out Pyodide wheels I have prepared a repo at https://github.com/ianthomas23/pyodide-wheels that uses wheels built from this branch and allows you to try it out for both Python 3.13 and 3.14 in a web browser of your choice. Also here is a screencast of it in action:
pyodide-backend.mp4
Historically a backend similar to this has sat in its own
matplotlib-pyodiderepo but it became awkward to maintain and was likely to disappear so I stepped in as I was aware that there could be a solution mostly relying onwebagg.Alternatives to this PR:
matplotlib-pyodide. To survive in a standalone repo it would need the solid commitment of a maintainer for say 10 years, and I have no interest in that but I am happy to maintain it as part of the core codebase here.Details
It is based on
webagg_corewhich remains fully backward-compatible with thewebaggandipymplbackends. The implementation followswebaggas much as possible. Inwebaggtornadois used on the Python side to serve resources (JavaScript, CSS, etc) to the JavaScript side in the browser, and they communicate via websockets. Inpyodidethe Python and JS code sit side-by-side and can essentially call each other directly (via pyodide converter shims really) and this code uses a mock websocket class to keep the code changes as small as possible.There is no testing yet. When we have reliable testing of wasm wheels I can add extra tests, similar to the
webaggtests, for this.Pinging pyodide devs @agriyakhetarpal, @hoodmane and @ryanking13 for awareness.
AI Disclosure
No AI used.
PR quality check