diff --git a/packages/polywrap-client/poetry.lock b/packages/polywrap-client/poetry.lock index 77e2f06a..890f6d9c 100644 --- a/packages/polywrap-client/poetry.lock +++ b/packages/polywrap-client/poetry.lock @@ -1,15 +1,15 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "astroid" -version = "2.15.2" +version = "2.15.3" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.2-py3-none-any.whl", hash = "sha256:dea89d9f99f491c66ac9c04ebddf91e4acf8bd711722175fe6245c0725cc19bb"}, - {file = "astroid-2.15.2.tar.gz", hash = "sha256:6e61b85c891ec53b07471aec5878f4ac6446a41e590ede0f2ce095f39f7d49dd"}, + {file = "astroid-2.15.3-py3-none-any.whl", hash = "sha256:f11e74658da0f2a14a8d19776a8647900870a63de71db83713a8e77a6af52662"}, + {file = "astroid-2.15.3.tar.gz", hash = "sha256:44224ad27c54d770233751315fa7f74c46fa3ee0fab7beef1065f99f09897efe"}, ] [package.dependencies] @@ -488,6 +488,24 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "polywrap-client-config-builder" +version = "0.1.0a28" +description = "" +category = "dev" +optional = false +python-versions = "^3.10" +files = [] +develop = true + +[package.dependencies] +polywrap-core = {path = "../polywrap-core", develop = true} +polywrap-uri-resolvers = {path = "../polywrap-uri-resolvers", develop = true} + +[package.source] +type = "directory" +url = "../polywrap-client-config-builder" + [[package]] name = "polywrap-core" version = "0.1.0a28" @@ -541,6 +559,25 @@ msgpack = "^1.0.4" type = "directory" url = "../polywrap-msgpack" +[[package]] +name = "polywrap-plugin" +version = "0.1.0a28" +description = "Plugin package" +category = "dev" +optional = false +python-versions = "^3.10" +files = [] +develop = true + +[package.dependencies] +polywrap-core = {path = "../polywrap-core", develop = true} +polywrap-manifest = {path = "../polywrap-manifest", develop = true} +polywrap-msgpack = {path = "../polywrap-msgpack", develop = true} + +[package.source] +type = "directory" +url = "../polywrap-plugin" + [[package]] name = "polywrap-uri-resolvers" version = "0.1.0a28" @@ -753,14 +790,14 @@ testutils = ["gitpython (>3)"] [[package]] name = "pyright" -version = "1.1.302" +version = "1.1.303" description = "Command line wrapper for pyright" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.302-py3-none-any.whl", hash = "sha256:1929e3126b664b5281dba66a789e8e04358afca48c10994ee0243b8c2a14acdf"}, - {file = "pyright-1.1.302.tar.gz", hash = "sha256:e74a7dfbbb1d754941d015cccea8a6d29b395d8e4cb0e45dcfcaf3b6c6cfd540"}, + {file = "pyright-1.1.303-py3-none-any.whl", hash = "sha256:8fe3d122d7e965e2df2cef64e1ceb98cff8200f458e7892d92a4c21ee85689c7"}, + {file = "pyright-1.1.303.tar.gz", hash = "sha256:7daa516424555681e8974b21a95c108c5def791bf5381522b1410026d4da62c1"}, ] [package.dependencies] @@ -803,14 +840,14 @@ files = [ [[package]] name = "pytest" -version = "7.3.0" +version = "7.3.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.3.0-py3-none-any.whl", hash = "sha256:933051fa1bfbd38a21e73c3960cebdad4cf59483ddba7696c48509727e17f201"}, - {file = "pytest-7.3.0.tar.gz", hash = "sha256:58ecc27ebf0ea643ebfdf7fb1249335da761a00c9f955bcd922349bcb68ee57d"}, + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, ] [package.dependencies] @@ -1224,4 +1261,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "868e9783a68c41710eb8195a5391e1ad1fb7b72f986060ad6974289c3dab01b1" +content-hash = "6f0a98c0051b3b8224458518af16bf6c692c4e3a4e92f468b7786c9c02d79617" diff --git a/packages/polywrap-client/pyproject.toml b/packages/polywrap-client/pyproject.toml index 02a81f9b..67e25440 100644 --- a/packages/polywrap-client/pyproject.toml +++ b/packages/polywrap-client/pyproject.toml @@ -15,9 +15,12 @@ polywrap-uri-resolvers = {path = "../polywrap-uri-resolvers", develop = true} polywrap-manifest = {path = "../polywrap-manifest", develop = true} polywrap-msgpack = {path = "../polywrap-msgpack", develop = true} polywrap-core = {path = "../polywrap-core", develop = true} + [tool.poetry.dev-dependencies] pytest = "^7.1.2" pytest-asyncio = "^0.19.0" +polywrap-plugin = {path = "../polywrap-plugin", develop = true} +polywrap-client-config-builder = {path = "../polywrap-client-config-builder", develop = true} pylint = "^2.15.4" black = "^22.10.0" bandit = { version = "^1.7.4", extras = ["toml"]} diff --git a/packages/polywrap-client/tests/cases/asyncify/wrap.info b/packages/polywrap-client/tests/cases/asyncify/wrap.info new file mode 100644 index 00000000..d77304d2 Binary files /dev/null and b/packages/polywrap-client/tests/cases/asyncify/wrap.info differ diff --git a/packages/polywrap-client/tests/cases/asyncify/wrap.wasm b/packages/polywrap-client/tests/cases/asyncify/wrap.wasm new file mode 100644 index 00000000..3ff58acd Binary files /dev/null and b/packages/polywrap-client/tests/cases/asyncify/wrap.wasm differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.info b/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.info new file mode 100644 index 00000000..5fe7559f Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.info differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.wasm b/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.wasm new file mode 100755 index 00000000..50a0ba0b Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/00-subinvoke/wrap.wasm differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.info b/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.info new file mode 100644 index 00000000..ea746854 Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.info differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.wasm b/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.wasm new file mode 100755 index 00000000..efba976d Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/01-invoke/wrap.wasm differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.info b/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.info new file mode 100644 index 00000000..b7303416 Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.info differ diff --git a/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.wasm b/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.wasm new file mode 100755 index 00000000..3b5e06c9 Binary files /dev/null and b/packages/polywrap-client/tests/cases/subinvoke/02-consumer/wrap.wasm differ diff --git a/packages/polywrap-client/tests/conftest.py b/packages/polywrap-client/tests/conftest.py index 906c9ce8..db1f91b1 100644 --- a/packages/polywrap-client/tests/conftest.py +++ b/packages/polywrap-client/tests/conftest.py @@ -1,16 +1,34 @@ from pathlib import Path -from polywrap_core import FileReader, ClientConfig -from polywrap_uri_resolvers import WRAP_MANIFEST_PATH, WRAP_MODULE_PATH, FsUriResolver, SimpleFileReader +from polywrap_core import FileReader, ClientConfig, Invoker, UriPackageOrWrapper, Env, Uri +from polywrap_uri_resolvers import ( + RecursiveResolver, + UriResolverAggregator, + StaticResolver, + FsUriResolver, + SimpleFileReader, + WRAP_MANIFEST_PATH, + WRAP_MODULE_PATH +) +from polywrap_plugin import PluginModule, PluginPackage from pytest import fixture +from typing import Dict, Any, Optional from polywrap_client import PolywrapClient - +import time @fixture -def client(): +def client(memory_storage_plugin: PluginPackage[None]): + memory_storage_uri = Uri.from_str("wrap://ens/memory-storage.polywrap.eth") config = ClientConfig( - resolver=FsUriResolver(file_reader=SimpleFileReader()) + resolver=RecursiveResolver( + UriResolverAggregator( + [ + FsUriResolver(file_reader=SimpleFileReader()), + StaticResolver({ memory_storage_uri: memory_storage_plugin}) + ] + ) ) + ) return PolywrapClient(config) @fixture @@ -36,4 +54,33 @@ async def read_file(self, file_path: str) -> bytes: return simple_wrap_manifest raise FileNotFoundError(f"FileNotFound: {file_path}") - yield SimpleFileReader() \ No newline at end of file + yield SimpleFileReader() + +class MemoryStorage(PluginModule[None]): + def __init__(self): + super().__init__(None) + self.value = 0 + + def getData(self, args: Dict[str, Any], client: Invoker[UriPackageOrWrapper], env: Optional[Env]) -> int: + time.sleep(0.05) # Sleep for 50 milliseconds + return self.value + + def setData(self, args: Dict[str, Any], client: Invoker[UriPackageOrWrapper], env: Optional[Env]) -> bool: + time.sleep(0.05) # Sleep for 50 milliseconds + self.value = args["value"] + return True + + +@fixture +def memory_storage_plugin() -> PluginPackage[None]: + return PluginPackage(module=MemoryStorage(), manifest={}) # type: ignore + + +class Adder(PluginModule[None]): + def add(self, args: Dict[str, Any], client: Invoker[UriPackageOrWrapper], env: Optional[Env]) -> int: + return args["a"] + args["b"] + + +@fixture +def adder_plugin() -> PluginPackage[None]: + return PluginPackage(module=Adder(None), manifest={}) # type: ignore diff --git a/packages/polywrap-client/tests/test_asyncify.py b/packages/polywrap-client/tests/test_asyncify.py new file mode 100644 index 00000000..63536585 --- /dev/null +++ b/packages/polywrap-client/tests/test_asyncify.py @@ -0,0 +1,111 @@ +# Polywrap Python Client - https://polywrap.io +# BigNumber wrapper schema - https://wrappers.io/v/ipfs/Qme2YXThmsqtfpiUPHJUEzZSBiqX3woQxxdXbDJZvXrvAD + +from pathlib import Path +from polywrap_client import PolywrapClient +from polywrap_core import Uri, InvokerOptions, UriPackageOrWrapper + + +async def test_asyncify(client: PolywrapClient): + uri = Uri.from_str( + f'fs/{Path(__file__).parent.joinpath("cases", "asyncify").absolute()}' + ) + args = { + "numberOfTimes": 40 + } + subsequent_invokes_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="subsequentInvokes", args=args + ) + subsequent_invokes_result = await client.invoke(subsequent_invokes_options) + subsequent_invokes_expected = [str(i) for i in range(40)] + + assert subsequent_invokes_result == subsequent_invokes_expected + + local_var_method_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="localVarMethod", args=None + ) + + local_var_method_result = await client.invoke(local_var_method_options) + + assert local_var_method_result == True + + global_var_method_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="globalVarMethod", args=None + ) + + global_var_method_result = await client.invoke(global_var_method_options) + assert global_var_method_result == True + + + large_str = "polywrap" * 10000 + set_data_with_large_args_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="setDataWithLargeArgs", args={"value":large_str} + ) + set_data_with_large_args_result = await client.invoke(set_data_with_large_args_options) + assert set_data_with_large_args_result == large_str + + large_str = "polywrap" * 10000 + set_data_with_large_args_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="setDataWithLargeArgs", args={"value":large_str} + ) + set_data_with_large_args_result = await client.invoke(set_data_with_large_args_options) + assert set_data_with_large_args_result == large_str + + set_data_with_many_args_args = { + "valueA": "polywrap a", + "valueB": "polywrap b", + "valueC": "polywrap c", + "valueD": "polywrap d", + "valueE": "polywrap e", + "valueF": "polywrap f", + "valueG": "polywrap g", + "valueH": "polywrap h", + "valueI": "polywrap i", + "valueJ": "polywrap j", + "valueK": "polywrap k", + "valueL": "polywrap l", + } + set_data_with_many_args_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="setDataWithManyArgs", args=set_data_with_many_args_args + ) + + set_data_with_many_args_result = await client.invoke(set_data_with_many_args_options) + + set_data_with_many_args_expected = "polywrap apolywrap bpolywrap cpolywrap dpolywrap epolywrap fpolywrap gpolywrap hpolywrap ipolywrap jpolywrap kpolywrap l" + assert set_data_with_many_args_result == set_data_with_many_args_expected + + def create_obj(i: int): + return { + "propA": f"a-{i}", + "propB": f"b-{i}", + "propC": f"c-{i}", + "propD": f"d-{i}", + "propE": f"e-{i}", + "propF": f"f-{i}", + "propG": f"g-{i}", + "propH": f"h-{i}", + "propI": f"i-{i}", + "propJ": f"j-{i}", + "propK": f"k-{i}", + "propL": f"l-{i}" + } + + set_data_with_many_structure_args_args = { + "valueA": create_obj(1), + "valueB": create_obj(2), + "valueC": create_obj(3), + "valueD": create_obj(4), + "valueE": create_obj(5), + "valueF": create_obj(6), + "valueG": create_obj(7), + "valueH": create_obj(8), + "valueI": create_obj(9), + "valueJ": create_obj(10), + "valueK": create_obj(11), + "valueL": create_obj(12), + } + set_data_with_many_structured_args_options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="setDataWithManyStructuredArgs", args=set_data_with_many_structure_args_args + ) + set_data_with_many_structured_args_result = await client.invoke(set_data_with_many_structured_args_options) + assert set_data_with_many_structured_args_result == True diff --git a/packages/polywrap-client/tests/test_client.py b/packages/polywrap-client/tests/test_client.py index db9badbd..c79d0406 100644 --- a/packages/polywrap-client/tests/test_client.py +++ b/packages/polywrap-client/tests/test_client.py @@ -1,8 +1,22 @@ from pathlib import Path + +from polywrap_client_config_builder import PolywrapClientConfigBuilder +from polywrap_plugin import PluginPackage from polywrap_client import PolywrapClient from polywrap_manifest import deserialize_wrap_manifest -from polywrap_core import Uri, InvokerOptions, FileReader, UriPackageOrWrapper, ClientConfig -from polywrap_uri_resolvers import BaseUriResolver, SimpleFileReader, StaticResolver +from polywrap_core import ( + Uri, + InvokerOptions, + FileReader, + UriPackageOrWrapper, + ClientConfig, +) +from polywrap_uri_resolvers import ( + BaseUriResolver, + FsUriResolver, + SimpleFileReader, + StaticResolver, +) from polywrap_wasm import WasmWrapper @@ -144,3 +158,38 @@ async def test_env(): result = await client.invoke(options) assert result == env + + +async def test_complex_subinvocation(adder_plugin: PluginPackage[None]): + config = ( + PolywrapClientConfigBuilder() + .add_resolver(FsUriResolver(SimpleFileReader())) + .set_redirect( + Uri.from_str("ens/imported-subinvoke.eth"), + Uri.from_str( + f'fs/{Path(__file__).parent.joinpath("cases", "subinvoke", "00-subinvoke").absolute()}' + ), + ) + .set_redirect( + Uri.from_str("ens/imported-invoke.eth"), + Uri.from_str( + f'fs/{Path(__file__).parent.joinpath("cases", "subinvoke", "01-invoke").absolute()}' + ), + ) + .set_package( + Uri.from_str("plugin/adder"), + adder_plugin, + ) + ).build() + + client = PolywrapClient(config) + uri = Uri.from_str( + f'fs/{Path(__file__).parent.joinpath("cases", "subinvoke", "02-consumer").absolute()}' + ) + args = {"a": 1, "b": 1} + options: InvokerOptions[UriPackageOrWrapper] = InvokerOptions( + uri=uri, method="addFromPluginAndIncrement", args=args + ) + result = await client.invoke(options) + + assert result == 4 diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py b/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py index bdf49e38..78ecd91f 100644 --- a/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py +++ b/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py @@ -1,13 +1,14 @@ """This module contains the subinvoke imports for the Wasm module.""" -from typing import Any, cast +import asyncio +from concurrent.futures import ThreadPoolExecutor from polywrap_core import InvokerOptions, Uri, WrapAbortError from polywrap_msgpack import msgpack_encode -from unsync import Unfuture from ..types import InvokeResult from .types import BaseWrapImports -from .utils import unsync_invoke + +pool = ThreadPoolExecutor() class WrapSubinvokeImports(BaseWrapImports): @@ -42,19 +43,17 @@ def wrap_subinvoke( args = self._get_subinvoke_args(args_ptr, args_len) try: - unfuture_result = cast( - Unfuture[Any], - unsync_invoke( - self.invoker, + result = pool.submit( + asyncio.run, + self.invoker.invoke( InvokerOptions( uri=uri, method=method, args=args, encode_result=True, - ), + ) ), - ) - result = unfuture_result.result() # type: ignore + ).result() if isinstance(result, bytes): self.state.subinvoke_result = InvokeResult(result=result) return True