Skip to content

wasi-python 0.1.0a6

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Oct 01:49
· 777 commits to main since this release
  • The x86_64 rebuild runs natively (spec 059). Every compile carries -ffp-contract=off,
    set once in the toolchain's own clang.cfg and clang++.cfg by 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 and MANIFEST change 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 -cc1 line. Spec 040's
    emulated compiler stays in the tree, switched off by one line in scripts/line.conf, until
    the first alpha whose native x86_64 rebuild passed has shipped. The LLVM issue draft is
    narrowed to llvm.fmuladd on fp128.

  • Async tools and gates (spec 053). A tool or a gate may be an async def, or a function
    that returns an awaitable. Under run_async it is awaited on the event loop that awaits
    run_async, so an MCP client session or an async HTTP client works as it is; under run, 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 next await. A KeyboardInterrupt, SystemExit or
    CancelledError a 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 an async def. An async generator is refused as a tool
    or a gate.

  • Changed: host= given an async def (or an async generator, a partial of one, an
    object with an async __call__) is refused with TypeError before 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 the Errno of that name would be. With no
    errno at all, FileNotFoundError, FileExistsError, IsADirectoryError,
    NotADirectoryError, PermissionError and TimeoutError answer ENOENT, EEXIST,
    EISDIR, ENOTDIR, EACCES and ETIMEDOUT. An errno outside the closed set (EINTR,
    EAGAIN and 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 6 gives 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.py compares 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= on run and run_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
    ToolBudgetExceeded and 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 per list frame. 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 with TypeError, where Python 3.14 raised RecursionError.

  • 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 larger max_tool_request_bytes, or None.

  • matplotlib, the first milestone (spec 023). matplotlib 3.11.2 is built in: import matplotlib; matplotlib.use("agg"), plt.plot(...) and savefig to 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 job plot_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's ft2font, _path, _image, _backend_agg,
    _c_internal_utils and _tri, kiwisolver 1.5.1 and Pillow 13.0.0's _imaging, _imagingmath
    and _imagingmorph (zlib only: JPEG and other codecs raise OSError). 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 (_qhull is left out of the link;
    explicit triangles work) and contour (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's lib/, so /usr/local/lib still 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.patch is upstream's
      merged commit 8e5591bc (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.patch and
      recipes/patches/kiwisolver-1.5.1/0001-callback-signatures.patch give 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/ and 19-kiwisolver-callback-signatures/.
    • The build: two new steps, 37 (the plot libraries) and 55 (Pillow, kiwisolver and
      matplotlib); the pinned pkgconf wheel in step 10; scripts/wasi-cc drops -pthread, which
      would have compiled +atomics in; 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
      kiwisolver UnsatisfiableConstraint; 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.
  • 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 its SHA256SUMS. Every job that checks, signs or publishes refuses an empty
    id, downloads by that id with digest-mismatch: error, and checks SHA256SUMS, 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.
    publish names the release environment, setup-uv's cache is off in every job, and every job
    states its own least permissions under a workflow-level permissions: {}. 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 into release-assets or into a job that holds it, any
    continue-on-error:, and a pypi job that could run before publish or outside a pushed tag.
    python.wasm, the snapshot and the zip do not change. MANIFEST changes in one line, because
    it records the download script's hash, so the set's SHA256SUMS and 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