Routing ideas

This commit is contained in:
Johan B.W. de Vries 2023-04-11 09:45:58 +02:00
parent 2485ccba40
commit bb9ac649bf
8 changed files with 140 additions and 47 deletions

View File

@ -0,0 +1,15 @@
from typing import List
from .image import Image
from .method import Method
class Container:
__slots__ = ('image', 'methods', )
image: Image
methods: List[Method]
def __init__(self, image: Image, methods: List[Method]) -> None:
self.image = image
self.methods = methods

View File

@ -0,0 +1,7 @@
class Image:
__slots__ = ('hash', )
hash: str
def __init__(self, hash: str) -> None:
self.hash = hash

View File

@ -0,0 +1,28 @@
from typing import List
from .valuetype import ValueType
class MethodArgument:
__slots__ = ('name', 'value_type', )
name: str
value_type: ValueType
def __init__(self, name: str, value_type: ValueType) -> None:
self.name = name
self.value_type = value_type
class Method:
__slots__ = ('name', 'args', 'return_type', )
name: str
args: List[MethodArgument]
return_type: ValueType
def __init__(self, name: str, args: List[MethodArgument], return_type: ValueType) -> None:
self.name = name
self.args = args
self.return_type = return_type

View File

@ -1,26 +1,22 @@
from typing import Any, Generic, TypeVar
from typing import Any, Union
T = TypeVar('T')
from .valuetype import ValueType
ValueData = Union[None, bytes]
class BaseValue(Generic[T]):
__slots__ = ('data', )
class Value:
__slots__ = ('value_type', 'data', )
data: T
value_type: ValueType
data: ValueData
def __init__(self, data: T) -> None:
def __init__(self, value_type: ValueType, data: ValueData) -> None:
self.value_type = value_type
self.data = data
def __eq__(self, other: Any) -> bool:
return self.__class__ is other.__class__ and self.data == other.data
return self.value_type is other.value_type and self.data == other.data
def __repr__(self) -> str:
return f'{self.__class__.__name__}({repr(self.data)})'
class UntypedValue(BaseValue[Any]):
pass
class BytesValue(BaseValue[bytes]):
pass
return f'Value(valuetype.{self.value_type.name}, {repr(self.data)})'

View File

@ -0,0 +1,12 @@
class ValueType:
__slots__ = ('name', )
name: str
def __init__(self, name: str) -> None:
self.name = name
bytes = ValueType('bytes')
none = ValueType('none')

View File

@ -1,15 +1,28 @@
from typing import Any
import sys
from phasmplatform.common import valuetype
from phasmplatform.common.config import from_toml
from phasmplatform.common.method import Method, MethodArgument
from phasmplatform.common.router import StdOutRouter
from phasmplatform.common.value import BaseValue, BytesValue
from phasmplatform.common.value import Value
from .runners.base import BaseRunner
from .runners.base import RunnerInterface
from .runners.wasmtime import WasmTimeRunner
def somefunc(runner: RunnerInterface) -> None:
inp = Value(valuetype.bytes, b'Hello, world!')
print('inp', inp)
def on_respond(out: Value) -> None:
print('out', out)
assert out == inp
echo = Method('echo', [MethodArgument('msg', valuetype.bytes)], valuetype.bytes)
runner.do_call(echo, [inp], on_respond)
def main() -> int:
with open('config.toml', 'rb') as fil:
config = from_toml(fil)
@ -18,26 +31,17 @@ def main() -> int:
stdout_router = StdOutRouter()
foo: BaseRunner
with open('/home/johan/projects/idea/phasm/examples/platform.wasm', 'rb') as fil:
foo = WasmTimeRunner(stdout_router, fil.read())
namespace = b'test-namespace'
topic = b'test-topic'
kind = b'test-kind'
body = b'test-body'
# namespace = b'test-namespace'
# topic = b'test-topic'
# kind = b'test-kind'
# body = b'test-body'
foo.handle_message(namespace, topic, kind, body)
# foo.handle_message(namespace, topic, kind, body)
inp = BytesValue(b'Hello, world!')
print('inp', inp)
def on_respond(out: BaseValue[Any]) -> None:
print('out', out)
assert out == inp
foo.do_call('echo', [inp], on_respond)
somefunc(foo)
return 0

View File

@ -1,9 +1,18 @@
from typing import TextIO
from typing import Callable, List, TextIO
from phasmplatform.common.router import BaseRouter
from phasmplatform.common.method import Method
from phasmplatform.common.value import Value
class BaseRunner:
class RunnerInterface:
__slots__ = ('router', )
def do_call(self, method: Method, args: List[Value], on_result: Callable[[Value], None]) -> None:
raise NotImplementedError
class BaseRunner(RunnerInterface):
__slots__ = ('router', )
router: BaseRouter

View File

@ -1,12 +1,15 @@
from typing import Any, Callable, List, Union
from typing import Callable, List, Union
import ctypes
import struct
import wasmtime
from phasmplatform.common import valuetype
from phasmplatform.common.method import Method
from phasmplatform.common.router import BaseRouter
from phasmplatform.common.value import BaseValue, BytesValue, UntypedValue
from phasmplatform.common.value import Value
from phasmplatform.common.valuetype import ValueType
from .base import BaseRunner
@ -66,20 +69,39 @@ class WasmTimeRunner(BaseRunner):
return raw[ptr + 4:ptr + 4 + length]
def convert_value(self, val: BaseValue[Any]) -> Union[int, float]:
if isinstance(val, BytesValue):
def value_to_wasm(self, val: Value) -> Union[None, int, float]:
if val.value_type is valuetype.bytes:
assert isinstance(val.data, bytes) # type hint
return self.alloc_bytes(val.data)
raise NotImplementedError(val)
def do_call(self, method_name: str, args: List[BaseValue[Any]], callback: Callable[[BaseValue[Any]], None]) -> None:
method = self.exports[method_name]
assert isinstance(method, wasmtime.Func)
def value_from_wasm(self, value_type: ValueType, val: Union[None, int, float]) -> Value:
if value_type is valuetype.bytes:
assert isinstance(val, int) # typ hint
return Value(valuetype.bytes, self.read_bytes(val))
act_args = [self.convert_value(x) for x in args]
result = method(self.store, *act_args)
raise NotImplementedError(value_type, val)
callback(UntypedValue(result)) # TODO: This returns a bytes pointer, but we can't detect that in advance
def do_call(self, method: Method, args: List[Value], on_result: Callable[[Value], None]) -> None:
wasm_method = self.exports[method.name]
assert isinstance(wasm_method, wasmtime.Func)
act_args = [self.value_to_wasm(x) for x in args]
result = wasm_method(self.store, *act_args)
assert result is None or isinstance(result, (int, float, )) # type hint
on_result(self.value_from_wasm(method.return_type, result))
# callback(UntypedValue(result)) # TODO: This returns a bytes pointer, but we can't detect that in advance
# def do_call(self, method_name: str, args: List[BaseValue[Any]], callback: Callable[[BaseValue[Any]], None]) -> None:
# method = self.exports[method_name]
# assert isinstance(method, wasmtime.Func)
# act_args = [self.convert_value(x) for x in args]
# result = method(self.store, *act_args)
# callback(UntypedValue(result)) # TODO: This returns a bytes pointer, but we can't detect that in advance
def handle_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None:
namespace_ptr = self.alloc_bytes(namespace)