Search code examples
rustwebassembly

Running markdown example with wit-bindgen


The WebAssembly Interface Types was removed in favor of wit-bindgen as discussed here: https://github.com/bytecodealliance/wasmtime/issues/677#issuecomment-1024087373.

I am trying to compile the old markdown demo using interface types support available at https://github.com/bytecodealliance/wasmtime-demos, and import the wasm file in a python runtime. While the compilation works, I am unable to import the module since the feature was removed. Therefore I am trying to rewrite it with wit-bindgen: https://github.com/bytecodealliance/wit-bindgen

This is my project setup:

Cargo.toml

[package]
name = "markdown"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ['cdylib']

[dependencies]
pulldown-cmark = "0.5.3"
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen" }

src/render.wit

render: func(name: string) -> string

src/lib.rs

use pulldown_cmark::{html, Parser};
wit_bindgen_rust::export!("./src/render.wit");

struct Render;

impl render::Render for Render {
    fn render(input: String) -> String {
        let parser = Parser::new(&input);
        let mut html_output = String::new();
        html::push_html(&mut html_output, parser);
        return html_output;
    }
}

I can compile this with cargo build and move the wasm file to the root directory with cp /target/wasm32-unknown-unknown/release/markdown.wasm .. The python bindings file bindings.py is generated by issuing wit-bindgen wasmtime-py --import src/render.wit.

How do I finally import the wasm file in python? I have tried the following:

import wasmtime.loader
import markdown

but this gives me the error:

File ~/.local/lib/python3.10/site-packages/wasmtime/loader.py:72, in _WasmtimeLoader.exec_module(self, module)
     70     break
     71 field_name = wasm_import.name
---> 72 imported_module = importlib.import_module(module_name)
     73 item = imported_module.__dict__[field_name]
     74 if not isinstance(item, Func) and \
     75         not isinstance(item, Table) and \
     76         not isinstance(item, Global) and \
     77         not isinstance(item, Memory):

File /usr/lib/python3.10/importlib/__init__.py:126, in import_module(name, package)
    124             break
    125         level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)

ModuleNotFoundError: No module named '__wbindgen_placeholder__'

I believe this is because the bindings.py file is not being used. How do i specify it using python-wasmtime ?


Solution

  • I recently did something similar with wit-bindgen and python.

    In your Python code you can do something like this to make it work:

    from wasmtime import Engine, Store, Config, Module, Linker, WasiConfig
    from md import bindings
    
    config = Config()
    engine = Engine(config)
    store = Store(engine)
    store.set_wasi(WasiConfig())
    module = Module.from_file(engine, path)
    linker = Linker(engine)
    linker.define_wasi()
    markdown = bindings.Markdown(store, linker, module)
    res = markdown.render(store, text)
    

    You can also take a look here for the full example: https://github.com/olavvatne/python-webassembly-rust