Files
wasmtime/docs/lang-python.md
2020-03-24 21:46:58 -05:00

3.9 KiB

Using WebAssembly from Python

Wasmtime can be used as a python module loader, which allows almost any WebAssembly module to be used as a python module. This guide will go over adding Wasmtime to your project, and some provided examples of what can be done with WebAssembly modules.

Prerequisites

To follow this guide, you'll need

Getting started and simple example

First, copy this example WebAssembly text module into your project. It exports a function for calculating the greatest common denominator of two numbers.

{{#include ../examples/gcd.wat}}

Before we can do anything with this module, we need to convert it to the WebAssembly binary format. We can do this with the command line tools provided by the WebAssembly binary toolkit

wat2wasm gcd.wat

This will create the binary form of the gcd module gcd.wasm, we'll use this module in the following steps.

Next, install the Wasmtime module loader, which is provided as a python package on PyPi. It can be installed as a dependency through Pip or related tools such as Pipenv.

pip install wasmtime

Or

pipenv install wasmtime

After you have Wasmtime installed and you've imported wasmtime, you can import WebAssembly modules in your project like any other python module.

{{#include ../crates/misc/py/examples/gcd/run.py}}

This script should output

gcd(27, 6) = 3

If this is the output you see, congrats! You've successfully ran your first WebAssembly code in python!

Host interaction and memory

In the first example, we called a function exported by a WebAssembly module. Depeding on what you need to accomplish, WebAssembly modules can also call functions from other modules and python itself. This is done through the module imports mechanism, which allows other modules and the host environment to provide functions, globals, and memory spaces. The following example will show you how to use module imports and work with module linear memory.

Note: At the moment, the Wasmtime python module can only import functions and memories.

To show how we can use functions from the host, take a look at this rust code

{{#include ../crates/misc/py/examples/import/demo.rs}}

We have a test function which calls callback. Since it's wrapped in extern "C", this function will be dynamically linked. The Wasmtime module does this linking automatically by importing any needed modules at runtime. If we compile this example without any extra linker options, the result module will import callback from a module called env, so we need to provide an implementation of callback inside an env.py module.

{{#include ../crates/misc/py/examples/import/env.py}}

The module provides callback with a pointer to a string message. We use this to index into the demo module's memory, extract the message bytes and print them as a string. Every WebAssembly module exports its main linear memory as "memory" by default, so it's accessible as demo.memory in python. We wrap the memory into a memoryview so we can safely access the values inside.

Before we move on, note the type annotations on callback. These are necessary for representing your function as something callable in WebAssembly, since WebAssembly functions only operate on 32 and 64 bit floats and integers. When defining functions for use by WebAssembly modules, make sure the parameters and return value are annotated appropriately as any of 'i32', 'i64', 'f32', or 'f64'.

Before we can use demo.rs we need to compile it

rustup run nightly rustc --target=wasm32-unknown-unknown --crate-type=cdylib demo.rs

We can then use it like this

{{#include ../crates/misc/py/examples/import/run.py}}

The script should print Hello, world! and exit.