Python is the second most popular programming language. Thanks to a few different efforts, it is now possible to compile the Python interpreter to WebAssembly. It is now possible to run Python inside of a WebAssembly runtime.
Brython is both a Python compiler and an interpreter written in JavaScript. As a result, you can compile and run Python code in the browser. A good example of this feature is demonstrated by the online editor available on the Brython website. With the online editor, Python is running in the browser.
To be able to compile it to WASM you need to add GOOS=js GOARCH=wasm to the build command. This tells Go to compile to a . wasm file.
First, let's take a look how, in principle, WebAssembly is different from asm.js, and whether there's potential to reuse existing knowledge and tooling. The following gives pretty good overview:
Let's recapitulate, WebAssembly (MVP, as there's more on its roadmap, roughly):
Thus, currently WebAssembly is an iteration on asm.js and targets only C/C++ (and similar languages).
It doesn't look like GC is the only thing that stops Python code from targeting WebAssembly/asm.js. Both represent low-level statically typed code, in which Python code can't (realistically) be represented. As current toolchain of WebAssembly/asm.js is based on LLVM, a language that can be easily compiled to LLVM IR can be converted to WebAssembly/asm.js. But alas, Python is too dynamic to fit into it as well, as proven by Unladen Swallow and several attempts of PyPy.
This asm.js presentation has slides about the state of dynamic languages. What it means is that currently it's only possible to compile whole VM (language implementation in C/C++) to WebAssembly/asm.js and interpret (with JIT where possible) original sources. For Python there're several existing projects:
PyPy: PyPy.js (author's talk at PyCon). Here's release repo. Main JS file, pypyjs.vm.js
, is 13 MB (2MB after gzip -6
) + Python stdlib + other stuff.
CPython: pyodide, EmPython, CPython-Emscripten, EmCPython, etc. empython.js
is 5.8 MB (2.1 MB after gzip -6
), no stdlib.
Micropython: this fork.
There was no built JS file there, so I was able to build it with trzeci/emscripten/
, a ready-made Emscripten toolchain. Something like:
git clone https://github.com/matthewelse/micropython.git
cd micropython
docker run --rm -it -v $(pwd):/src trzeci/emscripten bash
apt-get update && apt-get install -y python3
cd emscripten
make -j
# to run REPL: npm install && nodejs server.js
It produces micropython.js
of 1.1 MB (225 KB after gzip -d
). The latter is already something to consider, if you need only very compliant implementation without stdlib.
To produce WebAssembly build you can change line 13 of the Makefile
to
CC = emcc -s RESERVED_FUNCTION_POINTERS=20 -s WASM=1
Then make -j
produces:
113 KB micropython.js
240 KB micropython.wasm
You can look at HTML output of emcc hello.c -s WASM=1 -o hello.html
, to see how to use these files.
This way you can also potentially build PyPy and CPython in WebAssembly to interpret your Python application in a compliant browser.
Another potentially interesting thing here is Nuitka, a Python to C++ compiler. Potentially it can be possible to build your Python app to C++ and then compile it along with CPython with Emscripten. But practically I've no idea how to do it.
For the time being, if you're building a conventional web site or web app where download several-megabyte JS file is barely an option, take a look at Python-to-JavaScript transpilers (e.g. Transcrypt) or JavaScript Python implementations (e.g. Brython). Or try your luck with others from list of languages that compile to JavaScript.
Otherwise, if download size is not an issue, and you're ready to tackle a lot of rough edges, choose between the three above.
JavaScript port was integrated into MicroPython. It lives in ports/javascript.
The port is available as a npm package called MicroPython.js. You can try it out in RunKit.
There's an actively developed Python implementation in Rust, called RustPython. Because Rust officially supports WebAssembly as compile target, no surprise there's demo link right in the top of the readme. Though, it's early. Their disclaimer follows.
RustPython is in a development phase and should not be used in production or a fault intolerant setting.
Our current build supports only a subset of Python syntax.
In short: There are transpilers, but you can't automatically convert any arbitrary Python to Web Assembly, and I doubt you will be able to for a long time to come. Although theoretically the languages are equally powerful, and manual translation is always possible, Python allows for some data structures and expressive modes that requires a very smart inter-language compiler (or transpiler) [see below]. A workaround might be Python to C to Web Assembly since python-to-C technology is moderately mature, but that isn't generally going to work either since Python-to-C is also fragile (see below).
WebAssembly is specifically targeted to C-like languages as you can see at http://webassembly.org/docs/high-level-goals/
Translating from Python to C can be done with tools like PyPy, which has been under development for a long time, but which still does not work for arbitrary Python code. There are several reasons for this:
If you look more carefully into why Python-to-C (or Python to C++) has been so tricky you can see the detailed reasons behind this terse answer, but I think that's outside the scope of your question.
This won't be possible until web assembly implements garbage collection. You can follow progress here: https://github.com/WebAssembly/proposals/issues/16
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With