Repository navigation
wasi-python 0.1.0a2
Pre-release
Pre-release
·
1252 commits
to main
since this release
The second alpha moves the line to CPython 3.15.0 and wasi-sdk 33, so a C++ exception inside a
package becomes a Python exception instead of ending the guest.
- The line: CPython 3.15.0, built with wasi-sdk 33; numpy 2.5.2, pandas 3.0.6 and the
pure-Python packages are unchanged. The line's versions are named once, in
scripts/line.conf. - C++ exceptions work: numpy and pandas' C++ is compiled for WebAssembly exception handling
in its standard form (exnref). A C++ error such as pocketfft's failed allocation now raises
MemoryErrorin the program, which goes on. - Hosts must support exception handling. wasmtime 47 or later runs it by default (as
wasmtime-py does); wasmer, wazero and Node need a switch; WAMR and wasmi cannot run it. The
README's Hosts lists them. For a host without it, use0.1.0a1. - The stdlib zip's guest name is now
/usr/local/lib/python315.zip. An embedder that lays
out the root by hand must follow it;wasi_python.rundoes. - The snapshot is made by the pinned wasmtime's own
wasmtime wizer(the standalone Wizer
pin is gone), and pre-imports_colorize, which 3.15 loads lazily, so the first traceback in
a run does not pay for it. - Measured: a snapshot call costs about 6.6% more guest instructions than on 3.14 (81.9M
against 76.8M: CPython 3.15's own start-up and finalization); the stdlib zip is 6% larger. - Safe by default (
runchanges for existing callers):- with no
root, a call runs in a fresh private directory, removed afterwards; - a call has a 60 s deadline, a 1 GiB memory cap and a 16 MiB output cap per stream unless it
passes others (Nonemeans no limit); a longer job now ends withtrapped == "deadline"; - the guest's environment starts from
TMPDIR=/tmp,PYTHONTZPATH=and aHOMEmade
fresh for each call (under/tmp, removed after it), withenv=laid over it, so
os.environis no longer exactly whatenv=gave; - a call's store is closed when the call ends, trapped or not, so a trapped call no longer
holds its guest memory until Python's garbage collector runs; Completedgainstruncated, last, defaulting to().runrefuses a limit of the wrong type (TypeError) or out of range (ValueError) before
any work, where 0.1.0a1 passed it on: a negativefuelormemory_bytes, or a
memory_bytesof 263 or more, meant no limit;Truemeant 1; a deadline of 0 or less ran
one tick. Integer limits take any integer type (numpy's included) from 0 to 263 - 1; a
deadline is a positive real number of seconds (notDecimal), at most about 4.6e16.runrefuses anargv,envorstdinthe guest would see differently, before any work: a
NUL in an argument, key or value, or text that is not valid UTF-8 (ValueError); an=in a
key, or an empty key (ValueError); astrasargv, a non-stritem or entry, or anenv
that is not aMapping(TypeError).stdinmust be bytes-like:"",0,Falseand
[], which meant empty input, are now aTypeError, as any otherstralready was. Messages
never quote a value.
- with no
- Planted code never runs: only the stdlib zip is on
sys.pathandsitedoes not run
(sys.flags.no_site == 1), so a module,.pthorpyvenv.cfgone call leaves in a root is
never run by the next. The plain reactor ran them before. - The door is bounded: a frame past guest memory, a response over 2 GiB or a failing host is
a host fault (−2);runno longer raises for them. - Fuel counts the guest only:
fuel_usedand thefuelcap start after instantiation, so
they no longer include wasmtime's copy of the memory image (42.35M instructions for the
snapshot, 5.05M for the plain reactor, charged on macOS when compiled in the process and on
Windows always). Figures from 0.1.0a1 are that much higher. - A sleeping guest keeps its deadline:
time.sleepandselectno longer hold a call past
its deadline; the runner answers the guest's waits itself, stopping at the deadline. One
poll_oneofftakes at most 4,096 subscriptions (more answerENOMEM:select.poll()on more
than 2,047 descriptors), and a bad pointer answersEFAULT. - Concurrent calls share nothing: calls from many threads no longer corrupt wasmtime-py's
callback tables (after a few hundred concurrent calls, every later call raised, and a guest
could reach another call's host or output). The host functions are defined once per engine,
and outputs use the runner's own table. An exception a host raises, and Ctrl-C, now come back
to the call that caused them; Ctrl-C while a guest computed could abort the whole process. A
trap or exit inside the guest's allocator during a door call is that call's result, and its
fuel_usedisNone. Concurrent first calls compile the module once. - A guest cannot make
runraise by exhausting host file descriptors: the call's store,
which holds every descriptor the guest opened, is now closed before anything else is cleaned
up. Before,runraisedOSError(Errno 24) and left the stdin scratch file behind. - Release provenance: from this release on, every asset is attested by the release workflow
and verified before it is published; the notes say how to verify a download. - The snapshot honours extra preopens by name and in any order, as the plain reactor does;
before, it ignored them or swapped them. - The same bytes from any build host: arm64 macOS and arm64 Linux rebuild the same artifact
set from the pins, byte for byte, and the release workflow rebuilds it on an arm64 Linux runner
and comparesSHA256SUMSbefore anything is signed. x86_64 Linux is pinned and builds, but its
python.wasmis not yet byte-identical (one numpy function compiles differently there).
Every host links wasi-sdk's
host-neutral sysroot and builtins, and the build sorts in the C locale, sopython.wasm's hash
changes. Inside the guest,sysconfigandnumpy.show_config()name the build machine
build.MANIFEST'spinlines now list every supported build host's inputs.
Verify a download (gh logged in; see the README's Verifying a download):
gh attestation verify <asset> --repo ClayGendron/wasi_python \
--signer-workflow ClayGendron/wasi_python/.github/workflows/release.yml@refs/tags/v0.1.0a2 \
--source-ref refs/tags/v0.1.0a2 --deny-self-hosted-runners