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
|
||||
|
||||
The WAMR Python package contains a set of high-level bindings for WAMR API and WASM-C-API.
|
||||
|
||||
## Installation
|
||||
|
||||
### Installing from the source code
|
||||
|
||||
Installing from local source tree is in _development mode_. The package appears to be installed but still is editable from the source tree.
|
||||
To Install from local source tree in _development mode_ run the following command,
|
||||
|
||||
```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
|
||||
|
||||
```python
|
||||
import wamr.ffi as ffi
|
||||
From the same package you can use two set of APIs.
|
||||
|
||||
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`.
|
||||
|
||||
### 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.
|
||||
* [WAMR API](./wamr_api)
|
||||
* [WASM-C-API](./wasm_c_api)
|
||||
|
|
|
@ -8,7 +8,28 @@
|
|||
# pylint: disable=missing-function-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:
|
||||
|
@ -24,7 +45,11 @@ setup(
|
|||
long_description=readme,
|
||||
author="The WAMR Project Developers",
|
||||
author_email="hello@bytecodealliance.org",
|
||||
url="https://github.com/bytecodealliance/wamr-python",
|
||||
url="https://github.com/bytecodealliance/wasm-micro-runtime",
|
||||
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():
|
||||
current_file = Path(os.readlink(current_file))
|
||||
current_dir = current_file.parent.resolve()
|
||||
root_dir = current_dir.parent.parent.parent.parent.resolve()
|
||||
wamr_dir = root_dir.joinpath("wasm-micro-runtime").resolve()
|
||||
root_dir = current_dir.parents[4].resolve()
|
||||
wamr_dir = root_dir.resolve()
|
||||
if not wamr_dir.exists():
|
||||
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
|
||||
#
|
||||
import ctypes
|
||||
import wamr.ffi as ffi
|
||||
import wamr.wasmcapi.ffi as ffi
|
||||
|
||||
WAMS_BINARY_CONTENT = (
|
||||
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 unittest
|
||||
|
||||
import wamr.ffi as ffi
|
||||
import wamr.wasmcapi.ffi as ffi
|
||||
|
||||
|
||||
# It is a module likes:
|
|
@ -12,7 +12,7 @@ import ctypes as c
|
|||
import unittest
|
||||
from venv import create
|
||||
|
||||
from wamr.ffi import *
|
||||
from wamr.wasmcapi.ffi import *
|
||||
|
||||
# It is a module likes:
|
||||
# (module
|
|
@ -21,7 +21,7 @@ import sys
|
|||
from pycparser import c_ast, parse_file
|
||||
|
||||
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
|
||||
INDENT = " "
|
||||
|
||||
|
@ -314,7 +314,7 @@ class Visitor(c_ast.NodeVisitor):
|
|||
|
||||
|
||||
def preflight_check(workspace):
|
||||
wamr_repo = workspace.joinpath("wasm-micro-runtime")
|
||||
wamr_repo = workspace
|
||||
file_check_list = [
|
||||
wamr_repo.exists(),
|
||||
wamr_repo.joinpath(WASM_C_API_HEADER).exists(),
|
||||
|
@ -369,12 +369,12 @@ def main():
|
|||
current_file = pathlib.Path(os.readlink(current_file))
|
||||
|
||||
current_dir = current_file.parent.resolve()
|
||||
root_dir = current_dir.joinpath("..").resolve()
|
||||
root_dir = current_dir.joinpath("../../../..").resolve()
|
||||
|
||||
if not preflight_check(root_dir):
|
||||
return False
|
||||
|
||||
wamr_repo = root_dir.joinpath("wasm-micro-runtime")
|
||||
wamr_repo = root_dir
|
||||
binding_file_path = root_dir.joinpath(BINDING_PATH)
|
||||
with open(binding_file_path, "wt", encoding="utf-8") as binding_file:
|
||||
binding_file.write(do_parse(wamr_repo))
|
Loading…
Reference in New Issue
Block a user