Skip to main content

No project description provided

Project description

wasmbind

Wraps your WebAssembly exports to provide you are with a more usable interface in Python. Currently works with AssemblyScript modules, and python-ext-wasm as the loader.

In doing so, it tries to play a similar role as wasm-bindgen or as-bind in JavaScript.

Install with pip install wasmbind or poetry add wasmbind.

WARNING: As of this writing, the latest published version 0.3 of python-ext-wasm is not supported; you need to run on git master. The released version does not allow us to access the WASM memory.

Features

Features:

  • ✅ Strings, Arrays, Maps, Custom Classes.
  • ✅ Work with AssemblyScript objects in Python.
  • ✅ Instantiate new AssemblyScript objects in Python.

Future plans:

  • [ ] Allow wrapping arrays returned from WASM.
  • [ ] Improve array allocation by finding available types in RTTI.
  • [ ] Support imports (needs #28)
  • [ ] Improve these docs.
  • [ ] See if we can use RTTI to remove the need for a manual as_. We might have to create a class registry similar to as-bind
  • [ ] Investigate an alternative approach wherein you predefine classes (with types) in Python code.
  • [ ] Allow creation of types without a constructor.

Usage

Setup your module like this:

from wasmer import Instance
wasm = Instance(open('yourscript.wasm', 'rb').read())

from wasmbind import Module
module = Module(wasm)

Here are some sample interactions.

Strings

export function helloworld(name: string): string {
    return "hello, " + name
}
>>> module.helloworld("michael", as_=str)
"hello, michael"

You'll note that you have to specificy the desired return type via as_. This is because WASM only gives us a pointer to a memory location, and we otherwise have no idea what the type is. See the section Resolving Return Values for other options.

Passing values into AssemblyScript works, because we know it the type. In this case, we can allocate a string on the AssemblyScript side and pass the pointer to it into helloworld.

Note: You'll get a real Python str from AssemblyScript, and you are expected to pass real str objects to AssemblyScript functions. Strings are immutable in AssemblyScript and Python. Those things mean that for the boundary Python <-> AssemblyScript, they are passed by value and copied. No reference counting is involved.

Objects & Properties

export class File {
  constructor(
    public size: i32,
  ) {}
}
>>> dir = module.Directory(3)
>>> dir.size
3
>>> dir.size = 10
>>> dir.size
10

Objects

export class Line {
  constructor(
    public s: string
  ) {}
}

export class File {
  public lines: Line[] = []

  constructor() {}
  
  addLine(line: Line): number {
    this.lines.push(line);
    return this.lines.length; 
  }
}
>>> file = module.File()
>>> line = module.Line("line 1")
>>> file.addLine(line)
1

Maps and other generic types

Let's say you have a function that takes a map as an argument:

export function getMap(): Map<string, i32> {
  return new Map();
}

First, if you look into this module's exports, you will note that there is only getMap(). The Map class itself was not exported.

Now, if you add export {Map}, depending on your code, you might see exports such as:

'Map<~lib/string/String,~lib/string/String>#get', 'Map<i32,i32>#constructor', 'Map<i32,i32>#clear'

Every concrete version of the generic Map type is exported separately, the names aren't very nice, and finally, the classes are incomplete: Only methods which were used at some point in your code are exported, the rest, I assume, have been optimized away.

Currently, wasmbind does not do anything special with those exports, which means you can use them, but they are not very accessible.

The best way to use a map, which I have found so far, is this:

export class StringMap extends Map<string, string> {};

This will give you a complete and fully-functional StringMap class in Python.

Resolving Return Values

If you have a memory address, you can do:

module.resolve() or module.resolve(as_=T)

If you have an opaque AssemblyScriptObject, you can do obj.as_(T).

Possible values for as_:

  • If not given, we'll try to auto-detect.
  • str
  • Any AssemblyScriptClass exported by the module.
  • typing.List or typing.List[SomeOtherType], with SomeOtherType being any as value.

Options for the future:

# Every return value is a a Opaque Type that you can either call .native() on or .as().
module = Module(instance, value_handler=wrap_opaque)

# Every return value is auto-instantiated via the object header 
module = Module(instance, value_handler=auto_resolve)

# Using mypy to predefine the return types of each method and function call. 
module = Module(instance, class_registry={})

Opaque Values

Sometimes it can be nice to pass data structures to AssemblyScript that you just want to keep as-is, without AssemblyScript touching them, and getting them back; in particular, when dealing with complex data structures.

To help support this case, wasmbind supports a mechanism by which:

  • You can put an arbitrary Python value into a local registry.
  • You'll be given an opaque object that you can pass to AssemblyScript functions.
  • AssemblyScript will see an integer (we start counting at 1, so it's up to you if you want to use u8, u32, ...)
  • When a value comes out of AssemblyScript, you need to instruct wasmbind, using the regular mechanisms, to resolve this opaque pointer as a wasmbind.OpaqueValue instance.

Here is an example:

export function take(val: u8): u8 { return val; }
from wasmbind import OpaqueValue
my_map = {"x": 1}
wrapped_map = module.register_opaque_value(my_map)
assert module.take(wrapped_map, as_=OpaqueValue) == {"x": 1}

Notes

In part, this is a port of the AssemblyScript loader. The following links were helpful in implementing this:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for wasmbind, version 0.3
Filename, size File type Python version Upload date Hashes
Filename, size wasmbind-0.3-py3-none-any.whl (10.4 kB) File type Wheel Python version py3 Upload date Hashes View hashes
Filename, size wasmbind-0.3.tar.gz (12.8 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page