mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2024-11-26 15:32:05 +00:00
Add WAMR API bindings in Python (#1959)
Before adding the new bindings: 1. Moved wasm-c-api in a subfolder wasmcapi in the package. 2. Adapted the tests to be able to run in this new structure. New: 1. Added the WAMR API in another folder wamrapi in the same level as wasm-c-api. 2. Created an OOP proposal. 3. Added an example using this proposal.
This commit is contained in:
parent
f60c3c6111
commit
3cc132e8fc
1
language-bindings/python/MANIFEST.in
Normal file
1
language-bindings/python/MANIFEST.in
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include src/wamr/libs/*
|
|
@ -1,31 +1,34 @@
|
||||||
# wamr-python
|
# wamr-python
|
||||||
|
|
||||||
|
The WAMR Python package contains a set of high-level bindings for WAMR API and WASM-C-API.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Installing from the source code
|
To Install from local source tree in _development mode_ run the following command,
|
||||||
|
|
||||||
Installing from local source tree is in _development mode_. The package appears to be installed but still is editable from the source tree.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python -m pip install -e /path/to/wamr-root/binding/python
|
python -m pip install -e .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In this mode the package appears to be installed but still is editable from the source tree.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```python
|
From the same package you can use two set of APIs.
|
||||||
import wamr.ffi as ffi
|
|
||||||
|
To use the WAMR API you can import the symbols as follows,
|
||||||
|
|
||||||
|
```py
|
||||||
|
from wamr.wamrapi.wamr import Engine, Module, Instance, ExecEnv
|
||||||
```
|
```
|
||||||
|
|
||||||
### Preparation
|
In the order hand, to use the WASM-C-API,
|
||||||
|
|
||||||
The binding will load the shared library _libiwasm.so_ from the WAMR repo. So before running the binding, you need to build the library yourself.
|
```py
|
||||||
|
import wamr.wasmcapi.ffi as ffi
|
||||||
|
```
|
||||||
|
|
||||||
The default compile options are good enough.
|
For more information:
|
||||||
|
|
||||||
Please be aware that `wasm_frame_xxx` and `wasm_trap_xxx` only work well when enabling `WAMR_BUILD_DUMP_CALL_STACK`.
|
* [WAMR API](./wamr_api)
|
||||||
|
* [WASM-C-API](./wasm_c_api)
|
||||||
### Examples
|
|
||||||
|
|
||||||
There is a [simple example](./samples/hello_procedural.py) to show how to use bindings. Actually, the python binding follows C-APIs. There it should be easy if be familiar with _programming with wasm-c-api_.
|
|
||||||
|
|
||||||
Unit test cases under _./tests_ could be another but more complete references.
|
|
||||||
|
|
|
@ -8,7 +8,28 @@
|
||||||
# pylint: disable=missing-function-docstring
|
# pylint: disable=missing-function-docstring
|
||||||
# pylint: disable=missing-module-docstring
|
# pylint: disable=missing-module-docstring
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
import pathlib
|
||||||
|
from setuptools import setup
|
||||||
|
from setuptools.command.develop import develop
|
||||||
|
from setuptools.command.install import install
|
||||||
|
from subprocess import check_call
|
||||||
|
|
||||||
|
|
||||||
|
def build_library():
|
||||||
|
cur_path = pathlib.Path(__file__).parent
|
||||||
|
check_call(f"{cur_path}/utils/create_lib.sh".split())
|
||||||
|
|
||||||
|
class PreDevelopCommand(develop):
|
||||||
|
"""Pre-installation for development mode."""
|
||||||
|
def run(self):
|
||||||
|
build_library()
|
||||||
|
develop.run(self)
|
||||||
|
|
||||||
|
class PreInstallCommand(install):
|
||||||
|
"""Pre-installation for installation mode."""
|
||||||
|
def run(self):
|
||||||
|
build_library()
|
||||||
|
install.run(self)
|
||||||
|
|
||||||
|
|
||||||
with open("README.md") as f:
|
with open("README.md") as f:
|
||||||
|
@ -24,7 +45,11 @@ setup(
|
||||||
long_description=readme,
|
long_description=readme,
|
||||||
author="The WAMR Project Developers",
|
author="The WAMR Project Developers",
|
||||||
author_email="hello@bytecodealliance.org",
|
author_email="hello@bytecodealliance.org",
|
||||||
url="https://github.com/bytecodealliance/wamr-python",
|
url="https://github.com/bytecodealliance/wasm-micro-runtime",
|
||||||
license=license,
|
license=license,
|
||||||
packages=["wamr"],
|
include_package_data=True,
|
||||||
|
cmdclass={
|
||||||
|
'develop': PreDevelopCommand,
|
||||||
|
'install': PreInstallCommand,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
0
language-bindings/python/src/wamr/__init__.py
Normal file
0
language-bindings/python/src/wamr/__init__.py
Normal file
0
language-bindings/python/src/wamr/libs/.placeholder
Normal file
0
language-bindings/python/src/wamr/libs/.placeholder
Normal file
1779
language-bindings/python/src/wamr/wamrapi/iwasm.py
Normal file
1779
language-bindings/python/src/wamr/wamrapi/iwasm.py
Normal file
File diff suppressed because it is too large
Load Diff
123
language-bindings/python/src/wamr/wamrapi/wamr.py
Normal file
123
language-bindings/python/src/wamr/wamrapi/wamr.py
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
|
||||||
|
from ctypes import Array
|
||||||
|
from ctypes import c_char
|
||||||
|
from ctypes import c_uint
|
||||||
|
from ctypes import c_uint8
|
||||||
|
from ctypes import c_void_p
|
||||||
|
from ctypes import cast
|
||||||
|
from ctypes import create_string_buffer
|
||||||
|
from ctypes import POINTER
|
||||||
|
from ctypes import pointer
|
||||||
|
|
||||||
|
from wamr.wamrapi.iwasm import Alloc_With_Pool
|
||||||
|
from wamr.wamrapi.iwasm import RuntimeInitArgs
|
||||||
|
from wamr.wamrapi.iwasm import wasm_exec_env_t
|
||||||
|
from wamr.wamrapi.iwasm import wasm_function_inst_t
|
||||||
|
from wamr.wamrapi.iwasm import wasm_module_inst_t
|
||||||
|
from wamr.wamrapi.iwasm import wasm_module_t
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_call_wasm
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_create_exec_env
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_deinstantiate
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_destroy
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_destroy_exec_env
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_full_init
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_instantiate
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_load
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_lookup_function
|
||||||
|
from wamr.wamrapi.iwasm import wasm_runtime_unload
|
||||||
|
|
||||||
|
|
||||||
|
class Engine:
|
||||||
|
def __init__(self):
|
||||||
|
self.init_args = self._get_init_args()
|
||||||
|
wasm_runtime_full_init(pointer(self.init_args))
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
print("deleting Engine")
|
||||||
|
wasm_runtime_destroy()
|
||||||
|
|
||||||
|
def _get_init_args(self, heap_size: int = 1024 * 512) -> RuntimeInitArgs:
|
||||||
|
init_args = RuntimeInitArgs()
|
||||||
|
init_args.mem_alloc_type = Alloc_With_Pool
|
||||||
|
init_args.mem_alloc_option.pool.heap_buf = cast(
|
||||||
|
(c_char * heap_size)(), c_void_p
|
||||||
|
)
|
||||||
|
init_args.mem_alloc_option.pool.heap_size = heap_size
|
||||||
|
return init_args
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
__create_key = object()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_file(cls, engine: Engine, fp: str) -> "Module":
|
||||||
|
return Module(cls.__create_key, engine, fp)
|
||||||
|
|
||||||
|
def __init__(self, create_key: object, engine: Engine, fp: str) -> None:
|
||||||
|
assert (
|
||||||
|
create_key == Module.__create_key
|
||||||
|
), "Module objects must be created using Module.from_file"
|
||||||
|
self.engine = engine
|
||||||
|
self.module, self.file_data = self._create_module(fp)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
print("deleting Module")
|
||||||
|
wasm_runtime_unload(self.module)
|
||||||
|
|
||||||
|
def _create_module(self, fp: str) -> tuple[wasm_module_t, Array[c_uint]]:
|
||||||
|
with open(fp, "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
data = (c_uint8 * len(data))(*data)
|
||||||
|
|
||||||
|
error_buf = create_string_buffer(128)
|
||||||
|
module = wasm_runtime_load(data, len(data), error_buf, len(error_buf))
|
||||||
|
if not module:
|
||||||
|
raise Exception("Error while creating module")
|
||||||
|
return module, data
|
||||||
|
|
||||||
|
|
||||||
|
class Instance:
|
||||||
|
def __init__(self, module: Module, stack_size: int = 65536, heap_size: int = 16384):
|
||||||
|
self.module = module
|
||||||
|
self.module_inst = self._create_module_inst(module, stack_size, heap_size)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
print("deleting Instance")
|
||||||
|
wasm_runtime_deinstantiate(self.module_inst)
|
||||||
|
|
||||||
|
def lookup_function(self, name: str):
|
||||||
|
func = wasm_runtime_lookup_function(self.module_inst, name, None)
|
||||||
|
if not func:
|
||||||
|
raise Exception("Error while looking-up function")
|
||||||
|
return func
|
||||||
|
|
||||||
|
def _create_module_inst(self, module: Module, stack_size: int, heap_size: int) -> wasm_module_inst_t:
|
||||||
|
error_buf = create_string_buffer(128)
|
||||||
|
module_inst = wasm_runtime_instantiate(
|
||||||
|
module.module, stack_size, heap_size, error_buf, len(error_buf)
|
||||||
|
)
|
||||||
|
if not module_inst:
|
||||||
|
raise Exception("Error while creating module instance")
|
||||||
|
return module_inst
|
||||||
|
|
||||||
|
|
||||||
|
class ExecEnv:
|
||||||
|
def __init__(self, module_inst: Instance, stack_size: int = 65536):
|
||||||
|
self.module_inst = module_inst
|
||||||
|
self.exec_env = self._create_exec_env(module_inst, stack_size)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
print("deleting ExecEnv")
|
||||||
|
wasm_runtime_destroy_exec_env(self.exec_env)
|
||||||
|
|
||||||
|
def call(self, func: wasm_function_inst_t, argc: int, argv: "POINTER[c_uint]"):
|
||||||
|
if not wasm_runtime_call_wasm(self.exec_env, func, argc, argv):
|
||||||
|
raise Exception("Error while calling function")
|
||||||
|
|
||||||
|
def _create_exec_env(self, module_inst: Instance, stack_size: int) -> wasm_exec_env_t:
|
||||||
|
exec_env = wasm_runtime_create_exec_env(module_inst.module_inst, stack_size)
|
||||||
|
if not exec_env:
|
||||||
|
raise Exception("Error while creating execution environment")
|
||||||
|
return exec_env
|
|
@ -36,8 +36,8 @@ current_file = Path(__file__)
|
||||||
if current_file.is_symlink():
|
if current_file.is_symlink():
|
||||||
current_file = Path(os.readlink(current_file))
|
current_file = Path(os.readlink(current_file))
|
||||||
current_dir = current_file.parent.resolve()
|
current_dir = current_file.parent.resolve()
|
||||||
root_dir = current_dir.parent.parent.parent.parent.resolve()
|
root_dir = current_dir.parents[4].resolve()
|
||||||
wamr_dir = root_dir.joinpath("wasm-micro-runtime").resolve()
|
wamr_dir = root_dir.resolve()
|
||||||
if not wamr_dir.exists():
|
if not wamr_dir.exists():
|
||||||
raise RuntimeError(f"not found the repo of wasm-micro-runtime under {root_dir}")
|
raise RuntimeError(f"not found the repo of wasm-micro-runtime under {root_dir}")
|
||||||
|
|
17
language-bindings/python/utils/create_lib.sh
Executable file
17
language-bindings/python/utils/create_lib.sh
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
|
||||||
|
CUR_DIR=$(cd $(dirname $0) && pwd -P)
|
||||||
|
ROOT_DIR=${CUR_DIR}/../../..
|
||||||
|
|
||||||
|
WAMR_BUILD_PLATFORM=${WAMR_BUILD_PLATFORM:-"linux"}
|
||||||
|
|
||||||
|
cd ${ROOT_DIR}/product-mini/platforms/${WAMR_BUILD_PLATFORM}
|
||||||
|
|
||||||
|
mkdir -p build && cd build
|
||||||
|
cmake ..
|
||||||
|
make -j
|
||||||
|
|
||||||
|
cp libiwasm.so ${CUR_DIR}/../src/wamr/libs
|
25
language-bindings/python/wamr-api/README.md
Normal file
25
language-bindings/python/wamr-api/README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# WARM API
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Copy in `language-bindings/python/wamr/libs` the library `libiwasm` generated from `product-mini/platforms`.
|
||||||
|
|
||||||
|
There is a [simple example](./samples/main.py) to show how to use bindings.
|
||||||
|
|
||||||
|
```
|
||||||
|
python samples/main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update WAMR API bindings
|
||||||
|
|
||||||
|
Install requirements,
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the following command,
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ctypesgen ../../../../core/iwasm/include/wasm_export.h -l ../libs/libiwasm.so -o iwasm.py
|
||||||
|
```
|
1
language-bindings/python/wamr-api/requirements.txt
Normal file
1
language-bindings/python/wamr-api/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ctypesgen==1.1.1
|
11
language-bindings/python/wamr-api/samples/compile.sh
Normal file
11
language-bindings/python/wamr-api/samples/compile.sh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
|
||||||
|
/opt/wasi-sdk/bin/clang \
|
||||||
|
-O0 -z stack-size=4096 -Wl,--initial-memory=65536 \
|
||||||
|
-Wl,--strip-all,--no-entry -nostdlib \
|
||||||
|
-Wl,--export=sum\
|
||||||
|
-Wl,--allow-undefined \
|
||||||
|
-o test.wasm sum.c
|
22
language-bindings/python/wamr-api/samples/main.py
Normal file
22
language-bindings/python/wamr-api/samples/main.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
|
||||||
|
from wamr.wamrapi.wamr import Engine, Module, Instance, ExecEnv
|
||||||
|
from ctypes import c_uint
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
def main():
|
||||||
|
engine = Engine()
|
||||||
|
module = Module.from_file(engine, pathlib.Path(__file__).parent / "sum.wasm")
|
||||||
|
module_inst = Instance(module)
|
||||||
|
exec_env = ExecEnv(module_inst)
|
||||||
|
|
||||||
|
func = module_inst.lookup_function("sum")
|
||||||
|
|
||||||
|
argv = (c_uint * 2)(*[10, 11])
|
||||||
|
exec_env.call(func, len(argv), argv)
|
||||||
|
print(argv[0])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
12
language-bindings/python/wamr-api/samples/sum.c
Normal file
12
language-bindings/python/wamr-api/samples/sum.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
sum(int a, int b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
7
language-bindings/python/wasm-c-api/README.md
Normal file
7
language-bindings/python/wasm-c-api/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# WASM-C-API
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
There is a [simple example](./samples/hello_procedural.py) to show how to use bindings. Actually, the python binding follows C-APIs. There it should be easy if be familiar with _programming with wasm-c-api_.
|
||||||
|
|
||||||
|
Unit test cases under _./tests_ could be another but more complete references.
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
@ -5,7 +5,7 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
#
|
#
|
||||||
import ctypes
|
import ctypes
|
||||||
import wamr.ffi as ffi
|
import wamr.wasmcapi.ffi as ffi
|
||||||
|
|
||||||
WAMS_BINARY_CONTENT = (
|
WAMS_BINARY_CONTENT = (
|
||||||
b"\x00asm\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01`\x00\x00\x02\x8a\x80"
|
b"\x00asm\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01`\x00\x00\x02\x8a\x80"
|
|
@ -12,7 +12,7 @@ import ctypes as c
|
||||||
import math
|
import math
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import wamr.ffi as ffi
|
import wamr.wasmcapi.ffi as ffi
|
||||||
|
|
||||||
|
|
||||||
# It is a module likes:
|
# It is a module likes:
|
|
@ -12,7 +12,7 @@ import ctypes as c
|
||||||
import unittest
|
import unittest
|
||||||
from venv import create
|
from venv import create
|
||||||
|
|
||||||
from wamr.ffi import *
|
from wamr.wasmcapi.ffi import *
|
||||||
|
|
||||||
# It is a module likes:
|
# It is a module likes:
|
||||||
# (module
|
# (module
|
|
@ -21,7 +21,7 @@ import sys
|
||||||
from pycparser import c_ast, parse_file
|
from pycparser import c_ast, parse_file
|
||||||
|
|
||||||
WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h"
|
WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h"
|
||||||
BINDING_PATH = "wamr/binding.py"
|
BINDING_PATH = "language-bindings/python/wamr/wasmcapi/binding.py"
|
||||||
# 4 spaces as default indent
|
# 4 spaces as default indent
|
||||||
INDENT = " "
|
INDENT = " "
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ class Visitor(c_ast.NodeVisitor):
|
||||||
|
|
||||||
|
|
||||||
def preflight_check(workspace):
|
def preflight_check(workspace):
|
||||||
wamr_repo = workspace.joinpath("wasm-micro-runtime")
|
wamr_repo = workspace
|
||||||
file_check_list = [
|
file_check_list = [
|
||||||
wamr_repo.exists(),
|
wamr_repo.exists(),
|
||||||
wamr_repo.joinpath(WASM_C_API_HEADER).exists(),
|
wamr_repo.joinpath(WASM_C_API_HEADER).exists(),
|
||||||
|
@ -369,12 +369,12 @@ def main():
|
||||||
current_file = pathlib.Path(os.readlink(current_file))
|
current_file = pathlib.Path(os.readlink(current_file))
|
||||||
|
|
||||||
current_dir = current_file.parent.resolve()
|
current_dir = current_file.parent.resolve()
|
||||||
root_dir = current_dir.joinpath("..").resolve()
|
root_dir = current_dir.joinpath("../../../..").resolve()
|
||||||
|
|
||||||
if not preflight_check(root_dir):
|
if not preflight_check(root_dir):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
wamr_repo = root_dir.joinpath("wasm-micro-runtime")
|
wamr_repo = root_dir
|
||||||
binding_file_path = root_dir.joinpath(BINDING_PATH)
|
binding_file_path = root_dir.joinpath(BINDING_PATH)
|
||||||
with open(binding_file_path, "wt", encoding="utf-8") as binding_file:
|
with open(binding_file_path, "wt", encoding="utf-8") as binding_file:
|
||||||
binding_file.write(do_parse(wamr_repo))
|
binding_file.write(do_parse(wamr_repo))
|
Loading…
Reference in New Issue
Block a user