1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
MicroPython WebAssembly
=======================
MicroPython for [WebAssembly](https://webassembly.org/).
Dependencies
------------
Building the webassembly port bears the same requirements as the standard
MicroPython ports with the addition of Emscripten, and optionally terser for
the minified file.
The output includes `micropython.mjs` (a JavaScript wrapper for the
MicroPython runtime) and `micropython.wasm` (actual MicroPython compiled to
WASM).
Build instructions
------------------
In order to build `micropython.mjs`, run:
$ make
To generate the minified file `micropython.min.mjs`, run:
$ make min
Running with Node.js
--------------------
Access the repl with:
$ make repl
This is the same as running:
$ node build-standard/micropython.mjs
The initial MicroPython GC heap size may be modified using:
$ node build-standard/micropython.mjs -X heapsize=64k
Where stack size may be represented in bytes, or have a `k` or `m` suffix.
MicroPython scripts may be executed using:
$ node build-standard/micropython.mjs hello.py
Alternatively `micropython.mjs` may by accessed by other JavaScript programs in node
using the require command and the general API outlined below. For example:
```javascript
const mp_mjs = await import("micropython.mjs");
const mp = await mp_mjs.loadMicroPython();
mp.runPython("print('hello world')");
```
Or without await notation:
```javascript
import("micropython.mjs").then((mp_mjs) => {
mp_mjs.loadMicroPython().then((mp) => {
mp.runPython("print('hello world')");
});
});
```
Running with HTML
-----------------
The following code demonstrates the simplest way to load `micropython.mjs` in a
browser, create an interpreter context, and run some Python code:
```html
<!doctype html>
<html>
<head>
<script src="build-standard/micropython.mjs" type="module"></script>
</head>
<body>
<script type="module">
const mp = await loadMicroPython();
mp.runPython("print('hello world')");
</script>
</body>
</html>
```
The output in the above example will go to the JavaScript console. It's possible
to instead capture the output and print it somewhere else, for example in an
HTML element. The following example shows how to do this, and also demonstrates
the use of top-level await and the `js` module:
```html
<!doctype html>
<html>
<head>
<script src="build-standard/micropython.mjs" type="module"></script>
</head>
<body>
<pre id="micropython-stdout"></pre>
<script type="module">
const stdoutWriter = (line) => {
document.getElementById("micropython-stdout").innerText += line + "\n";
};
const mp = await loadMicroPython({stdout:stdoutWriter});
await mp.runPythonAsync(`
import js
url = "https://api.github.com/users/micropython"
print(f"fetching {url}...")
res = await js.fetch(url)
json = await res.json()
for i in dir(json):
print(f"{i}: {json[i]}")
`);
</script>
</body>
</html>
```
MicroPython code execution will suspend the browser so be sure to atomize usage
within this environment. Unfortunately interrupts have not been implemented for the
browser.
Testing
-------
Run the test suite using:
$ make test
API
---
The following functions have been exposed to JavaScript through the interpreter
context, created and returned by `loadMicroPython()`.
- `PyProxy`: the type of the object that proxies Python objects.
- `FS`: the Emscripten filesystem object.
- `globals`: an object exposing the globals from the Python `__main__` module,
with methods `get(key)`, `set(key, value)` and `delete(key)`.
- `registerJsModule(name, module)`: register a JavaScript object as importable
from Python with the given name.
- `pyimport`: import a Python module and return it.
- `runPython(code)`: execute Python code and return the result.
- `runPythonAsync(code)`: execute Python code and return the result, allowing for
top-level await expressions (this call must be await'ed on the JavaScript side).
- `replInit()`: initialise the REPL.
- `replProcessChar(chr)`: process an incoming character at the REPL.
- `replProcessCharWithAsyncify(chr)`: process an incoming character at the REPL,
for use when ASYNCIFY is enabled.
Type conversions
----------------
Read-only objects (booleanns, numbers, strings, etc) are converted when passed between
Python and JavaScript. The conversions are:
- JavaScript `null` converts to/from Python `None`.
- JavaScript `undefined` converts to/from Python `js.undefined`.
The conversion between `null` and `None` matches the behaviour of the Python `json`
module.
Proxying
--------
A Python `dict` instance is proxied such that:
for (const key in dict) {
print(key, dict[key]);
}
works as expected on the JavaScript side and iterates through the keys of the
Python `dict`. Furthermore, when JavaScript accesses a key that does not exist
in the Python dict, the JavaScript code receives `undefined` instead of a
`KeyError` exception being raised.
|