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.

Source Distribution

wasmbind-0.4.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wasmbind-0.4-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file wasmbind-0.4.tar.gz.

File metadata

  • Download URL: wasmbind-0.4.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.2 CPython/3.7.3 Darwin/20.0.0

File hashes

Hashes for wasmbind-0.4.tar.gz
Algorithm Hash digest
SHA256 a82ad0d745c419d6cca1ebfb334048d31b8a9a43418399b45a8a9c41ff915c86
MD5 c9b302b9a78523c6b42e6dcb678b0b3c
BLAKE2b-256 0145d0adb551cff5a5bb9218274e4077728a04dcab4293c52cee29b08b6e13b2

See more details on using hashes here.

File details

Details for the file wasmbind-0.4-py3-none-any.whl.

File metadata

  • Download URL: wasmbind-0.4-py3-none-any.whl
  • Upload date:
  • Size: 10.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.2 CPython/3.7.3 Darwin/20.0.0

File hashes

Hashes for wasmbind-0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 ad336299e820d533816548551ce0312c959564a4c95805ac1f6a291b1aac46ec
MD5 8fdd586a1f69520838d4d9830c8c3409
BLAKE2b-256 1db56018dd2cf3b92ccf031dd88e7b0a60740053bf4a8e28fc9531d2b0adc03a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page