Skip to content

A WASM Plugin in Python (componentize-py)

A fidius WASM plugin is an ordinary WebAssembly component that implements an interface's WIT contract. Nothing about it is Rust-specific — any language that can produce a component works. This guide implements the same greeter interface as Your First WASM Plugin (Rust) in Python and loads it through the identical host path. This is the concrete polyglot payoff of the Component Model (ADR FIDIUS-A-0003, "Path B").

The worked example here is the committed fixture tests/wasm-fixtures/greeter-py/.

How non-Rust plugins differ from the Rust flow

A Rust author gets the WIT generated by the macros. A non-Rust author works from the WIT directly:

  1. Obtain the interface's .wit (published by the interface author; the Rust macros emit the same shape — see the WASM Component ABI).
  2. Implement the exported interface in your language.
  3. Build a component with your toolchain (here, componentize-py).
  4. Package, sign, and load exactly like any other fidius package.

There is no fidius dependency in the guest — it only has to satisfy the WIT.

Prerequisites

1. The WIT contract

This is the greeter interface projected to WIT (tests/wasm-fixtures/greeter/wit/world.wit). A fallible method maps to result<T, plugin-error>, a #[wire(raw)] method to list<u8>, and every plugin exports fidius-interface-hash so the host can verify it matches the descriptor at load:

package fidius:greeter@1.0.0;

interface greeter {
    record plugin-error { code: string, message: string, details: option<string> }
    greet: func(name: string) -> string;
    add: func(a: s64, b: s64) -> result<s64, plugin-error>;
    echo-bytes: func(data: list<u8>) -> list<u8>;
    fidius-interface-hash: func() -> u64;
}

world greeter-plugin {
    export greeter;
}

2. Implement it in Python

componentize-py maps the exported interface to a Python class. Kebab-case WIT names become snake_case methods; result<T, _> returns the Ok value directly (raise for the error arm):

# app.py
import os


class Greeter:
    """Implements the exported `greeter` interface."""

    def greet(self, name: str) -> str:
        return f"Hello, {name}!"

    def add(self, a: int, b: int) -> int:
        return a + b  # the Ok arm of result<s64, plugin-error>

    def echo_bytes(self, data: bytes) -> bytes:
        return bytes(reversed(data))

    def fidius_interface_hash(self) -> int:
        # MUST equal the interface's hash — the host rejects a mismatch at load.
        return 0x0102_0304_0506_0708

The interface hash must match

fidius-interface-hash is an integrity check: the host compares it to the descriptor's interface_hash and refuses the plugin on a mismatch. The interface author publishes the expected value (the Rust macros compute it from the method signatures). It is not a security boundary — signing is.

3. Build the component

componentize-py -d path/to/wit -w greeter-plugin componentize app -o greeter_py.wasm
wasm-tools validate --features component-model greeter_py.wasm

The result is a component exporting fidius:greeter/greeter — the same artifact shape the Rust plugin produces (it is larger, since it embeds a Python runtime).

4. Package, sign, and load

From here it is identical to any fidius package. The [wasm] section names the component and its capability allow-list:

# greeter-py-pkg/package.toml
[package]
name = "greeter-py-pkg"
version = "0.1.0"
interface = "greeter"
interface_version = 1
runtime = "wasm"

[metadata]
category = "demo"

[wasm]
component = "greeter_py.wasm"
capabilities = []
cp greeter_py.wasm greeter-py-pkg/
fidius package sign --key mykey.secret greeter-py-pkg
fidius package pack greeter-py-pkg
// The host loads it through the SAME API as the Rust guest.
let handle = host.load_wasm("greeter-py-pkg", &Greeter_WASM_DESCRIPTOR)?;
let greeting: String = handle.call_method(0, &("Ada".to_string(),))?;
assert_eq!(greeting, "Hello, Ada!");

The host neither knows nor cares that the component is Python — it loads, sandboxes, and dispatches it exactly like the Rust one. That is the polyglot guarantee.

See also