8978102f35
Previously to this commit, running the test suite on a bare-metal board required specifying the target (really platform) and device, eg: $ ./run-tests.py --target pyboard --device /dev/ttyACM1 That's quite a lot to type, and you also need to know what the target platform is, when a lot of the time you either don't care or it doesn't matter. This commit makes it easier to run the tests by replacing both of these options with a single `--test-instance` (`-t` for short) option. That option specifies the executable/port/device to test. Then the target platform is automatically detected. The `--test-instance` can be passed: - "unix" (the default) to use the unix version of MicroPython - "webassembly" to test the webassembly port - anything else is considered a port/device to pass to Pyboard There are also some shortcuts to specify a port/device, following `mpremote`: - a<n> is short for /dev/ttyACM<n> - u<n> is short for /dev/ttyUSB<n> - c<n> is short for COM<n> For example: $ ./run-tests.py -t a1 Note that the default test instance is "unix" and so this commit does not change the standard way to run tests on the unix port, by just doing `./run-tests.py`. As part of this change, the platform (and it's native architecture if it supports importing native .mpy files) is show at the start of the test run. Signed-off-by: Damien George <damien@micropython.org> |
||
---|---|---|
.. | ||
asyncio | ||
variants | ||
api.js | ||
lexer_dedent.c | ||
lexer_dedent.h | ||
library.h | ||
library.js | ||
main.c | ||
Makefile | ||
modjs.c | ||
modjsffi.c | ||
modtime.c | ||
mpconfigport.h | ||
mphalport.c | ||
mphalport.h | ||
node_run.sh | ||
objjsproxy.c | ||
objpyproxy.js | ||
proxy_c.c | ||
proxy_c.h | ||
proxy_js.js | ||
qstrdefsport.h | ||
README.md |
MicroPython WebAssembly
MicroPython for WebAssembly.
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:
const mp_mjs = await import("micropython.mjs");
const mp = await mp_mjs.loadMicroPython();
mp.runPython("print('hello world')");
Or without await notation:
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:
<!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:
<!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 methodsget(key)
,set(key, value)
anddelete(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 PythonNone
. - JavaScript
undefined
converts to/from Pythonjs.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.