Repository navigation
wasi-python 0.1.0a6
Pre-release-
The x86_64 rebuild runs natively (spec 059). Every compile carries
-ffp-contract=off,
set once in the toolchain's ownclang.cfgandclang++.cfgby step 10, so wasi-sdk's x86_64
and aarch64 builds of clang emit the same bytes, and the x86_64 Linux rebuild compiles with its
own clang instead of the arm64 clang under qemu (an hour less per release run). Run-time
arithmetic does not change (wasm32 fuses nothing); a constant the compiler folds moves to the
value the run time computes.python.wasm, the snapshot andMANIFESTchange once; the zip
stays. The judge refuses any compiler option that would override the file (-ffp-model=,
-ffast-math,-Ofast,-funsafe-math-optimizations,--no-default-config,--config,
another-ffp-contract=) in the recipe and in every build record, greps the source trees for
a pragma that turns contraction on, and probes the compiler's own-cc1line. Spec 040's
emulated compiler stays in the tree, switched off by one line inscripts/line.conf, until
the first alpha whose native x86_64 rebuild passed has shipped. The LLVM issue draft is
narrowed tollvm.fmuladdonfp128. -
Async tools and gates (spec 053). A tool or a gate may be an
async def, or a function
that returns an awaitable. Underrun_asyncit is awaited on the event loop that awaits
run_async, so an MCP client session or an async HTTP client works as it is; underrun, on a
loop of the call's own, on a thread started at the call's first awaitable and closed when the
call ends. A call answered before its coroutine started never starts it (spec 056's fix,
extended to async tools); every later end of the call (timeout_s, the deadline, a cancel,
Ctrl-C) cancels it at its nextawait. AKeyboardInterrupt,SystemExitor
CancelledErrora tool raises, and a loop that has closed or stopped, are faults in the record,
never exceptions in the embedder's loop. The guest, the protocol and the artifact set do not
change. The README's MCP recipe is now anasync def. An async generator is refused as a tool
or a gate. -
Changed:
host=given anasync def(or an async generator, apartialof one, an
object with an async__call__) is refused withTypeErrorbefore any work. The raw door is
synchronous; it used to answer every frame with a host fault and never await the coroutine. -
A provider may raise
OSError(spec 061). Its errno, matched by name (never by number,
which differs by host), is the guest's answer, as theErrnoof that name would be. With no
errno at all,FileNotFoundError,FileExistsError,IsADirectoryError,
NotADirectoryError,PermissionErrorandTimeoutErroranswerENOENT,EEXIST,
EISDIR,ENOTDIR,EACCESandETIMEDOUT. An errno outside the closed set (EINTR,
EAGAINand the rest), whatever the class, and any other exception stay a fault,EIO. A
mapped raise leaves no fault in the record. The guest never sees the exception's message, and
the record keeps nothing of it. -
The judge runs on several cores (spec 052).
uv run pytest -n 6gives the serial
answer in about a third of the time: pytest-xdist 3.8.0 is pinned in the development group,
each check's record is made on its worker and carried to the controller, which alone writes the
run record and accepts, and an upstream group runs with its classes in one worker. The lane's
accepting run stays serial;tests/harness/sameness.pycompares two run records. Five checks
no longer depend on the order or speed of the run, and the judge's zips folder is safe when
several processes make it at once. Nothing in the artifact set or the wheel changes. -
Security: a per-run budget of tool request bytes (spec 060; the 0.1.0a4 security review's
F3; spec 049, Decision 8, exception 2).max_tool_request_bytes=onrunandrun_async, and
Defaults.tool_request_bytes, default 16 MiB, counted from each frame's length before it is
read, whatever its answer; a frame that would pass it is refused unread, and the guest gets
ToolBudgetExceededand runs on. Parsing a 1 MiB frame costs the host a few tenths of a second
of CPU, so until now only the deadline bounded one call's total. The tool list is encoded once
per call, not once perlistframe.tool()finds a partial's bound keywords through a
__call__chain of any depth and through a metaclass's__call__(the review's N8), and
refuses a__call__cycle withTypeError, where Python 3.14 raisedRecursionError. -
Changed: a new, tighter default (spec 060; spec 049, Decision 8, exception 1): a call that
sends more than 16 MiB of tool requests passes a largermax_tool_request_bytes, orNone. -
matplotlib, the first milestone (spec 023). matplotlib 3.11.2 is built in:
import matplotlib; matplotlib.use("agg"),plt.plot(...)andsavefigto PNG, PDF, PS, EPS or SVG
in a granted folder. A fixed line plot's RGBA buffer equals native matplotlib's at the same pins,
and its PNG decodes to those pixels (the judge's golden jobplot_line). Linked in: FreeType
2.14.3, HarfBuzz 14.1.0, SheenBidi 3.0.0 and libraqm 0.10.5 (matplotlib's own wrap versions, for
native pixel parity), matplotlib'sft2font,_path,_image,_backend_agg,
_c_internal_utilsand_tri, kiwisolver 1.5.1 and Pillow 13.0.0's_imaging,_imagingmath
and_imagingmorph(zlib only: JPEG and other codecs raiseOSError). In the zip: matplotlib,
mpl_toolkits, PIL, kiwisolver, fontTools 4.67.0, cycler 0.12.1, pyparsing 3.3.3 and packaging
26.3, each with its METADATA. Not yet: Delaunay triangulation (_qhullis left out of the link;
explicit triangles work) andcontour(contourpy), both the next milestone; the snapshot does not
pre-import matplotlib (the owner's question 34).- matplotlib's data travels inside the zip as one member,
matplotlib/mpl-data.zip. The
runner extracts it once per process into its private zip folder, checks every file as it
writes it, compares the whole tree with its record on every call, and grants it read-only at
/usr/local/lib/python315.zip/matplotlib/mpl-data, the path matplotlib computes. The zip
itself now sits in that folder'slib/, so/usr/local/libstill lists only the zip
(spec 017's Decision 2, amended). An embedder without the runner extracts and preopens it
itself (the README says how). - Three more carried patches.
recipes/patches/matplotlib-3.11.2/0001-ft2font-enums-without-python.patchis upstream's
merged commit8e5591bc(pull request #30291, matplotlib 3.12):ft2font's enums no longer
take the GIL during static initialization, which hung a statically linked reactor before Python
started.recipes/patches/pillow-13.0.0/0001-arrow-capsule-signatures.patchand
recipes/patches/kiwisolver-1.5.1/0001-callback-signatures.patchgive two Pillow methods and
21 kiwisolver methods and getters the signatures CPython calls them with: spec 051's callback
scan found them in the first matplotlib link, and kiwisolver's trapped the guest at
Solver.addConstraint. Facts sheets for the owner:context/upstream/17-…(nothing to send),
18-pillow-arrow-capsule-signatures/and19-kiwisolver-callback-signatures/. - The build: two new steps, 37 (the plot libraries) and 55 (Pillow, kiwisolver and
matplotlib); the pinnedpkgconfwheel in step 10;scripts/wasi-ccdrops-pthread, which
would have compiled+atomicsin; step 60 checks the static constructors and target features
of every link against the judge's reviewed lists and stops on a new one. Eighteen new pins,
among them the Unicode License V3 for HarfBuzz's tables, pinned at a commit of Unicode's own
repository. - The judge: the milestone job and its PNG decoder (stdlib only);
savefig(dpi=-1)and a
kiwisolverUnsatisfiableConstraint; the data grant refuses every write; the static
constructor list; the ten target features; fifteen source trees judged against their tarballs
(the release workflow's count is 15); every C++ build's exception flags; the licence notices of
every new component. The callback scan no longer reads a record whose function sits at a parser
rule's table index as a method definition (FreeType's BDF property table). Two known gaps,
closed by the research memo's M2.
- matplotlib's data travels inside the zip as one member,
-
Security: the release signs and publishes only what its build job made (spec 057; the
0.1.0a4 security review's F4, N1 and N3's workflow side; it closes the one-release allowance the
0.1.0a5 addendum gave F4, before this release). The build job hands on its upload's artifact id
and the SHA-256 of itsSHA256SUMS. Every job that checks, signs or publishes refuses an empty
id, downloads by that id withdigest-mismatch: error, and checksSHA256SUMS, every file, and
that nothing else is there, before it acts; until now each fetched whatever artifact was named
release-assets. The two Linux rebuilds are two jobs instead of a matrix, so one cannot hand on
the other's ids. The download step fails, naming the file, when a fetched file has no pin.
publishnames thereleaseenvironment,setup-uv's cache is off in every job, and every job
states its own least permissions under a workflow-levelpermissions: {}. The judge checks all
of it, runs the workflow's own check and guard steps, and runs the download step on fake pins.
It also refuses any other download intorelease-assetsor into a job that holds it, any
continue-on-error:, and apypijob that could run beforepublishor outside a pushed tag.
python.wasm, the snapshot and the zip do not change.MANIFESTchanges in one line, because
it records the download script's hash, so the set'sSHA256SUMSand the wheel change too.
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.0a6 \
--source-ref refs/tags/v0.1.0a6 --deny-self-hosted-runners