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 adder world with an add function.
package docs:adder@0.1.0;
interface add {
add: func(x: u32, y: u32) -> u32;
}
world adder {
export add;
}
Create a new directory for your project and create a subdirectory in it called wit.
Copy and paste this code into a file named wit/component.wit.
If you want to generate bindings 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 wit --world adder bindings .
note
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.
If you attempt to run bindings generation twice, it will fail if the bindings folder already exists.
Bindings are generated in a folder called wit_world by default:
<project folder>
├── wit
│ └── component.wit
└── wit_world
├── exports
│ ├── add.py
│ └── __init__.py
├── __init__.py
└── types.py
The wit_world/exports folder contains an Add protocol which has an add method that we can implement,
which represents the export defined in the adder world WIT.
To implement the adder world (in particular the add interface that is exported),
put the following code in a file called app.py:
from wit_world import exports
class Add(exports.Add):
def add(self, x: int, y: int) -> int:
return x + y
We now can compile our application to a WebAssembly component using the componentize subcommand:
componentize-py \
--wit-path wit/component.wit \
--world adder \
componentize \
app \
-o add.wasm
Component built successfully
Running components from the example host
The following section requires you to have a Rust toolchain installed.
This repository contains an example WebAssembly host written in Rust
that can run components that implement the adder world.
git clone https://github.com/bytecodealliance/component-docs.gitcd component-docs/component-model/examples/example-hostcargo run --release -- 1 2 <PATH>/adder.wasm
- The double dashes separate the flags passed to
cargofrom the flags passed in to your code. - The arguments 1 and 2 are the arguments to the adder.
- In place of
<PATH>, substitute the directory that contains your generatedadder.wasmfile.
Note: When hosts run components that use WASI interfaces, they must explicitly add WASI to the linker to run the built component.
A successful run should show the following output (of course, the paths to your example host and adder component will vary):
cargo run --release -- 1 2 adder.wasm
Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
Finished `release` profile [optimized] target(s) in 7.85s
Running `target/debug/example-host 1 2 /path/to/adder.wasm`
1 + 2 = 3
If not configured correctly, you may see errors like the following:
cargo run --release -- 1 2 adder.wasm
Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
Finished `release` profile [optimized] target(s) in 7.85s
Running `target/release/example-host 1 2 /path/to/adder.component.wasm`
Error: Failed to instantiate the example world
Caused by:
0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker
1: instance export `error` has the wrong type
2: resource implementation is missing
This kind of error normally indicates that the host in question does not satisfy WASI imports.
See componentize-py's examples
to try out building HTTP, CLI, and TCP components from Python applications.
Running components from Python Applications
WebAssembly components can also be invoked from Python applications.
This section walks through using python native tooling to call the pre-built add.wasm component in the examples.
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.
If necessary, remove your add.wasm file that you generated in the previous section.
# 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.
Copy/paste the following code into a file called host.py:
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
You can find another example of a Python host usage via wasmtime-py in the componentize-py repository.