Python Tooling
Building a Component with componentize-py
componentize-py
is a tool that converts a Python
application to a WebAssembly component.
First, install Python 3.10 or later and pip if you don't already have them. Then, install componentize-py
:
pip install componentize-py
Next, create or download the WIT world you would like to target. For this example we will use an example
world with an add
function:
package example:component;
world example {
export add: func(x: s32, y: s32) -> s32;
}
If you want to generate bindings produced for the WIT world (for an IDE or typechecker), you can generate them using the bindings
subcommand. Specify the path to the WIT interface with the world you are targeting:
$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example bindings .
You do not need to generate the bindings in order to
componentize
in the next step.componentize
will generate bindings on-the-fly and bundle them into the produced component.
You can see that bindings were created in an example
package which contains an Example
protocol with an add
method that we can implement:
$ cat<<EOT >> app.py
import example
class Example(example.Example):
def add(self, x: int, y: int) -> int:
return x + y
EOT
We now can compile our application to a Wasm component using the componentize
subcommand:
$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example componentize app -o add.wasm
Component built successfully
To test the component, run it using the Rust add
host:
$ cd component-model/examples/add-host
$ cargo run --release -- 1 2 ../path/to/add.wasm
1 + 2 = 3
See componentize-py
's examples to try out build HTTP, CLI, and TCP components from Python applications.
Building a Component that Exports an Interface
The sample add.wit
file exports a function. However, to use your component from another component, it must export an interface. That being said, you rarely find WIT that does not contain an interface. (Most WITs you'll see in the wild do use interfaces; we've been simplifying by exporting a function.) Let's expand our example world to export an interface rather than directly export the function.
// add-interface.wit
package example:component;
interface add {
add: func(a: u32, b: u32) -> u32;
}
world example {
export add;
}
If you peek at the bindings, you'll notice that we now implement a class for the add
interface rather than for the example
world. This is a consistent pattern. As you export more interfaces from your world, you implement more classes. Our add example gets the slight update of:
# app.py
import example
class Add(example.Example):
def add(self, a: int, b: int) -> int:
return a + b
Once again, compile an application to a Wasm component using the componentize
subcommand:
$ componentize-py --wit-path add-interface.wit --world example componentize app -o add.wasm
Component built successfully
Running components from Python Applications
Wasm components can also be invoked from Python applications. This walks through using tooling
to call the app.wasm
component from the examples.
wasmtime-py
does not currently support running components build withcomponentize-py
. This is becausewasmtime-py
does not yet support resources, which components built withcomponentize-py
always use, sincecomponentize-py
unconditionally imports most of thewasi:cli
world.
First, install Python 3.11 or later and pip if you don't already have them. Then, install wasmtime-py
:
$ pip install wasmtime
First, generate the bindings to be able to call the component from a Python host application.
# Get an add component that does not import the WASI CLI world
$ wget https://github.com/bytecodealliance/component-docs/raw/main/component-model/examples/example-host/add.wasm
$ python3 -m wasmtime.bindgen add.wasm --out-dir add
The generated package add
has all of the requisite exports/imports for the
component and is annotated with types to assist with type-checking and
self-documentation as much as possible. Inside the package is a Root
class
with an add
function that calls the component's exported add
function. We
can now write a Python program that calls add
:
from add import Root
from wasmtime import Store
def main():
store = Store()
component = Root(store)
print("1 + 2 = ", component.add(store, 1, 2))
if __name__ == '__main__':
main()
Run the Python host program:
$ python3 host.py
1 + 2 = 3