-
Notifications
You must be signed in to change notification settings - Fork 36
Improved Rust py example #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a93c1bf
feat: Add the wlr-assets crate
assambar 7b3b358
feat: Add wlr-libpy crate
assambar 9467af6
feat: Revamp python/examples/embedding
assambar eed86a2
Apply suggestions from code review
assambar 13d18bb
fix: libpy review comments
assambar 10baf1c
fix wasi-py-rs review comments
assambar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
This folder offers various examples of how to use Python in wasm modules. | ||
|
||
- [./basic](./basic/) is a collection of small snippets that demonstrate how to run `python.wasm` from the command line or via [Docker](https://docs.docker.com/get-docker/) | ||
- [./embedding/wasi-command-rs](./embedding/) shows how one can embed the static wasm32-wasi `libpython` into a command Wasm module using Rust. | ||
- [./embedding/wasi-py-rs](./embedding/) shows how one can embed the static wasm32-wasi `libpython` into a command Wasm module using Rust. | ||
- [./bindings](./bindings/) is a sample application that demonstrates how one can use host-to-python and python-to-host bindings | ||
|
||
Note: `build.rs` does not have any download or performance optimization - the necessary `wasm32-wasi` dependencies are fetched and unpacked on each build run. |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
57 changes: 46 additions & 11 deletions
57
...ples/embedding/wasi-command-rs/Cargo.lock → .../examples/embedding/wasi-py-rs/Cargo.lock
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "wasi-py-rs" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
pyo3 = { version = "0.19.0", features = ["abi3-py311"] } | ||
wlr-libpy = { git = "https://github.com/vmware-labs/webassembly-language-runtimes.git", features = ["py_main"] } | ||
|
||
[build-dependencies] | ||
wlr-libpy = { git = "https://github.com/vmware-labs/webassembly-language-runtimes.git", features = ["build"] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# About | ||
|
||
Example that embeds CPython via libpython into a Wasm module written in Rust. | ||
|
||
Offers a couple of WASI Command (exporting `_start`) Wasm modules, written in Rust and demonstrates interaction with simple Python code via [pyo3](https://pyo3.rs/v0.19.0/). | ||
|
||
# How to run | ||
|
||
Make sure you have `cargo` with the `wasm32-wasi` target. For running we use `wasmtime`, but the module will work with any WASI-compliant runtime. | ||
|
||
Just run `./run_me.sh` in the current folder. You will see something like this | ||
|
||
``` | ||
wlr/python/examples/embedding/wasi-py-rs $$ ./run_me.sh | ||
Compiling pyo3-build-config v0.18.3 | ||
... | ||
Finished dev [unoptimized + debuginfo] target(s) in 26.43s | ||
|
||
Calling a WASI Command which embeds Python (adding a custom module implemented in Rust) and calls a custom function: | ||
+ wasmtime --mapdir /usr::target/wasm32-wasi/wasi-deps/usr target/wasm32-wasi/debug/py-func-caller.wasm | ||
Hello from Python (libpython3.11.a / 3.11.3 (tags/v3.11.3:f3909b8, Apr 28 2023, 09:45:45) [Clang 15.0.7 ]) in Wasm(Rust). | ||
args= (('John', 21, ['male', 'student']), ('Jane', 22, ['female', 'student']), ('George', 75, ['male', 'retired'])) | ||
Original people: [Person(Name: "John", Age: 21, Tags:["male", "student"]), Person(Name: "Jane", Age: 22, Tags:["female", "student"]), Person(Name: "George", Age: 75, Tags:["male", "retired"])] | ||
Filtered people by `student`: [Person(Name: "John", Age: 21, Tags:["male", "student"]), Person(Name: "Jane", Age: 22, Tags:["female", "student"])] | ||
+ set +x | ||
|
||
Calling a WASI Command which wraps the Python binary (adding a custom module implemented in Rust): | ||
+ wasmtime --mapdir /usr::target/wasm32-wasi/wasi-deps/usr target/wasm32-wasi/debug/py-wrapper.wasm -- -c 'import person as p; pp = [p.Person("a", 1), p.Person("b", 2)]; pp[0].add_tag("X"); print("Filtered: ", p.filter_by_tag(pp, "X"))' | ||
Filtered: [Person(Name: "a", Age: 1, Tags:["X"])] | ||
+ set +x | ||
``` | ||
|
||
# About the code | ||
|
||
To see how you can expand this example start with `pyo3`'s documentation on [calling Python from Rust](https://pyo3.rs/v0.18.3/python_from_rust). | ||
|
||
The main purpose here is to show how to configure the build and dependencies. | ||
|
||
The code has the following structure: | ||
|
||
``` | ||
src | ||
├── bin | ||
│ ├── py-func-caller.rs - A WASI command which defines and calls a Python function that uses the "person" module. | ||
│ └── py-wrapper.rs - A wrapper around Py_Main, which adds "person" as a built-in module. | ||
├── lib.rs - A library that allows one to call a Python function (passed as text) with arbitrary Rust Tuple arguments. | ||
└── py_module.rs - A simple Python module ("person") implemented in Rust. Offers creation and tagging of people via the person.Person class. | ||
``` | ||
|
||
# Build and dependencies | ||
|
||
For pyo3 to work the final binary needs to link to `libpython3.11.a`. The WLR project provides a pre-build `libpython` static library (based on [wasi-sdk](https://github.com/WebAssembly/wasi-sdk)), which depends on `wasi-sdk`. To setup the build properly you will need to provide several static libs and configure the linker to use them properly. | ||
|
||
We provide a helper crate [wlr-libpy](../../../tools/wlr-libpy/), which can be used to fetch the pre-built libpython. | ||
|
||
Take a look at [Cargo.toml](./Cargo.toml) to see how to add it as a build dependency: | ||
|
||
```toml | ||
[build-dependencies] | ||
wlr-libpy = { git = "https://github.com/vmware-labs/webassembly-language-runtimes.git", features = ["build"] } | ||
``` | ||
|
||
Then, in the [build.rs](./build.rs) file we only need to add this to the `main` method: | ||
|
||
```rs | ||
fn main() { | ||
// ... | ||
use wlr_libpy::bld_cfg::configure_static_libs; | ||
configure_static_libs().unwrap().emit_link_flags(); | ||
// ... | ||
} | ||
``` | ||
|
||
This will ensure that all required libraries are downloaded and the linker is configured to use them. | ||
|
||
Here is a diagram of the relevant dependencies | ||
|
||
```mermaid | ||
graph LR | ||
wasi_py_rs["wasi-py-rs"] --> wlr_libpy["wlr-libpy"] | ||
wlr_libpy --> wlr_assets["wlr-assets"] | ||
wlr_assets --> wasi_sysroot["wasi-sysroot-19.0.tar.gz"] | ||
wlr_assets --> clang_builtins["libclang_rt.builtins-wasm32-wasi-19.0.tar.gz"] | ||
wlr_assets --> libpython["libpython-3.11.3-wasi-sdk-19.0.tar.gz"] | ||
|
||
wasi_sysroot --> libwasi-emulated-signal.a | ||
wasi_sysroot --> libwasi-emulated-getpid.a | ||
wasi_sysroot --> libwasi-emulated-process-clocks.a | ||
clang_builtins --> libclang_rt.builtins-wasm32.a | ||
libpython --> libpython3.11.a | ||
|
||
wasi_py_rs["wasi-py-rs"] --> pyo3["pyo3"] | ||
pyo3 --> pyo3-ffi | ||
pyo3-ffi -..-> libpython3.11.a | ||
``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
fn main() { | ||
use wlr_libpy::bld_cfg::configure_static_libs; | ||
configure_static_libs().unwrap().emit_link_flags(); | ||
} |
File renamed without changes.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love this!