diff --git a/component-model/examples/tutorial/python/app.py b/component-model/examples/tutorial/python/app.py new file mode 100644 index 00000000..d9cfd1a1 --- /dev/null +++ b/component-model/examples/tutorial/python/app.py @@ -0,0 +1,5 @@ +from wit_world import exports + +class Add(exports.Add): + def add(self, x: int, y: int) -> int: + return x + y diff --git a/component-model/examples/tutorial/python/host.py b/component-model/examples/tutorial/python/host.py new file mode 100644 index 00000000..f32d962f --- /dev/null +++ b/component-model/examples/tutorial/python/host.py @@ -0,0 +1,10 @@ +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() diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/python.md index 624a6c5f..60a9829d 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/python.md @@ -2,30 +2,38 @@ ## Building a Component with `componentize-py` -[`componentize-py`](https://github.com/bytecodealliance/componentize-py) is a tool that converts a Python +[`componentize-py`](https://github.com/bytecodealliance/componentize-py) is a tool +that converts a Python application to a WebAssembly component. -First, install [Python 3.10 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install `componentize-py`: +First, install [Python 3.10 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) +if you don't already have them. Then, install `componentize-py`: ```sh 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][adder-wit] with an `add` function (e.g. `wit/component.wit`): +Next, create or download the WIT world you would like to target. +For this example we will use an [`adder` world][adder-wit] with an `add` function. ```wit {{#include ../../examples/tutorial/wit/adder/world.wit}} ``` -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: +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: ```console 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. +> 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. @@ -46,18 +54,14 @@ Bindings are generated in a folder called `wit_world` by default: 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`: +To implement the `adder` world (in particular the `add` interface that is exported), +put the following code in a file called `app.py`: ```py -from wit_world import exports - -class Add(exports.Add): - def add(self, x: int, y: int) -> int: - return x + y +{{#include ../../examples/tutorial/python/app.py}} ``` -We now can compile our application to a Wasm component using the `componentize` subcommand: +We now can compile our application to a WebAssembly component using the `componentize` subcommand: ```console componentize-py \ @@ -69,30 +73,44 @@ componentize-py \ Component built successfully ``` -To test the component, run it using the [Rust `add` host](./rust.md#creating-a-command-component-with-cargo-component): +## Running components from the example host -```sh -$ cd component-model/examples/add-host -$ cargo run --release -- 1 2 ../path/to/add.wasm -1 + 2 = 3 -``` +The following section requires you to have [a Rust toolchain][rust] installed. + +{{#include example-host-part1.md}} -See [`componentize-py`'s examples](https://github.com/bytecodealliance/componentize-py/tree/main/examples) to try out build HTTP, CLI, and TCP components from Python applications. +A successful run should show the following output +(of course, the paths to your example host and adder component will vary): + +{{#include example-host-part2.md}} + +[rust]: https://www.rust-lang.org/learn/get-started + +See [`componentize-py`'s examples](https://github.com/bytecodealliance/componentize-py/tree/main/examples) +to try out building HTTP, CLI, and TCP components from Python applications. ## Running components from Python Applications -Wasm components can also be invoked from Python applications. This section walks through using tooling -to call the [pre-built `app.wasm` component][add-wasm] in the examples. +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][add-wasm] in the examples. -> `wasmtime-py` is only able to run components built with `componentize-py` when the `--stub-wasi` option is used at build time. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), and `componentize-py` by default generates components which use resources from the `wasi:cli` world. See [this example](https://github.com/bytecodealliance/componentize-py/tree/main/examples/sandbox) of using the `--stub-wasi` option to generate a `wasmtime-py`-compatible component. +> To use `wasmtime-py` to run a component built with `componentize-py`, +> the `--stub-wasi` option must have been passed to `componentize-py` +> when the component was built. +> This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), +> and `componentize-py` by default generates components which use resources from the `wasi:cli` world. +> See [this example](https://github.com/bytecodealliance/componentize-py/tree/main/examples/sandbox) +> of using the `--stub-wasi` option to generate a `wasmtime-py`-compatible component. -First, install [Python 3.11 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install [`wasmtime-py`](https://github.com/bytecodealliance/wasmtime-py): +First, install [Python 3.11 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. +Then, install [`wasmtime-py`](https://github.com/bytecodealliance/wasmtime-py): ```sh $ 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. ```sh # Get an add component that does not import the WASI CLI world @@ -100,23 +118,15 @@ $ wget https://github.com/bytecodealliance/component-docs/raw/main/component-mod $ 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`: +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`: ```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() +{{#include ../../examples/tutorial/python/host.py}} ``` Run the Python host program: