diff --git a/packages/polywrap-client/tests/test_plugin_wrapper.py b/packages/polywrap-client/tests/test_plugin_wrapper.py new file mode 100644 index 00000000..9906e2b8 --- /dev/null +++ b/packages/polywrap-client/tests/test_plugin_wrapper.py @@ -0,0 +1 @@ +# Encode - Decode need to be tested \ No newline at end of file diff --git a/packages/polywrap-client/tests/test_wasm_wrapper.py b/packages/polywrap-client/tests/test_wasm_wrapper.py new file mode 100644 index 00000000..e69de29b diff --git a/packages/polywrap-core/poetry.lock b/packages/polywrap-core/poetry.lock index 97622e7a..80d92f84 100644 --- a/packages/polywrap-core/poetry.lock +++ b/packages/polywrap-core/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.4" +version = "2.15.5" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.4-py3-none-any.whl", hash = "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347"}, - {file = "astroid-2.15.4.tar.gz", hash = "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8"}, + {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, + {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, ] [package.dependencies] @@ -455,14 +455,14 @@ files = [ [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] @@ -506,18 +506,18 @@ files = [ [[package]] name = "platformdirs" -version = "3.5.0" +version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.0-py3-none-any.whl", hash = "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4"}, - {file = "platformdirs-3.5.0.tar.gz", hash = "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"}, + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] @@ -655,21 +655,6 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] -[[package]] -name = "pydeps" -version = "1.12.3" -description = "Display module dependencies" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pydeps-1.12.3-py3-none-any.whl", hash = "sha256:d828ab178244f147119a4aa3757a0ffa062e2069b1ac3c244f3013bfdfe0aa5e"}, - {file = "pydeps-1.12.3.tar.gz", hash = "sha256:3f14f2d5ef04df050317a6ed4747e7fefd14399f9decfe5114271541b1b8ffe7"}, -] - -[package.dependencies] -stdlib-list = "*" - [[package]] name = "pydocstyle" version = "6.3.0" @@ -734,14 +719,14 @@ testutils = ["gitpython (>3)"] [[package]] name = "pyright" -version = "1.1.306" +version = "1.1.309" description = "Command line wrapper for pyright" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.306-py3-none-any.whl", hash = "sha256:008eb2a29584ae274a154d749cf81476a3073fb562a462eac8d43a753378b9db"}, - {file = "pyright-1.1.306.tar.gz", hash = "sha256:16d5d198be64de497d5f9002000a271176c381e21b977ca5566cf779b643c9ed"}, + {file = "pyright-1.1.309-py3-none-any.whl", hash = "sha256:a8b052c1997f7334e80074998ea0f93bd149550e8cf27ccb5481d3b2e1aad161"}, + {file = "pyright-1.1.309.tar.gz", hash = "sha256:1abcfa83814d792a5d70b38621cc6489acfade94ebb2279e55ba1f394d54296c"}, ] [package.dependencies] @@ -774,24 +759,6 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] -[[package]] -name = "pytest-asyncio" -version = "0.19.0" -description = "Pytest support for asyncio" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"}, - {file = "pytest_asyncio-0.19.0-py3-none-any.whl", hash = "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa"}, -] - -[package.dependencies] -pytest = ">=6.1.0" - -[package.extras] -testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] - [[package]] name = "pyyaml" version = "6.0" @@ -863,19 +830,19 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -914,31 +881,16 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -[[package]] -name = "stdlib-list" -version = "0.8.0" -description = "A list of Python Standard Libraries (2.6-7, 3.2-9)." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "stdlib-list-0.8.0.tar.gz", hash = "sha256:a1e503719720d71e2ed70ed809b385c60cd3fb555ba7ec046b96360d30b16d9f"}, - {file = "stdlib_list-0.8.0-py3-none-any.whl", hash = "sha256:2ae0712a55b68f3fbbc9e58d6fa1b646a062188f49745b495f94d3310a9fdd3e"}, -] - -[package.extras] -develop = ["sphinx"] - [[package]] name = "stevedore" -version = "5.0.0" +version = "5.1.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "stevedore-5.0.0-py3-none-any.whl", hash = "sha256:bd5a71ff5e5e5f5ea983880e4a1dd1bb47f8feebbb3d95b592398e2f02194771"}, - {file = "stevedore-5.0.0.tar.gz", hash = "sha256:2c428d2338976279e8eb2196f7a94910960d9f7ba2f41f3988511e95ca447021"}, + {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, + {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, ] [package.dependencies] @@ -1049,14 +1001,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.0-py3-none-any.whl", hash = "sha256:6ad00b63f849b7dcc313b70b6b304ed67b2b2963b3098a33efe18056b1a9a223"}, + {file = "typing_extensions-4.6.0.tar.gz", hash = "sha256:ff6b238610c747e44c268aa4bb23c8c735d665a63726df3f9431ce707f2aa768"}, ] [[package]] @@ -1184,4 +1136,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "60e51d986fcb9814b054fb0b6c5a6eea0c84829dc8db4e5cf3c592ade9f5353c" +content-hash = "bd0557df35270b1dcdb727e3505fbd3bdd1afaed488458de3346783456248e00" diff --git a/packages/polywrap-core/polywrap_core/types/__init__.py b/packages/polywrap-core/polywrap_core/types/__init__.py index 219b5d5f..1ce2c342 100644 --- a/packages/polywrap-core/polywrap_core/types/__init__.py +++ b/packages/polywrap-core/polywrap_core/types/__init__.py @@ -1,16 +1,12 @@ """This module contains all the core types used in the various polywrap packages.""" from .client import * from .config import * -from .env import * from .errors import * from .file_reader import * from .invocable import * -from .invoke_args import * from .invoker import * from .invoker_client import * -from .options import * from .uri import * -from .uri_like import * from .uri_package import * from .uri_package_wrapper import * from .uri_resolution_context import * diff --git a/packages/polywrap-core/polywrap_core/types/client.py b/packages/polywrap-core/polywrap_core/types/client.py index b73fa72c..5df1f119 100644 --- a/packages/polywrap-core/polywrap_core/types/client.py +++ b/packages/polywrap-core/polywrap_core/types/client.py @@ -1,25 +1,19 @@ """This module contains the Client interface.""" from __future__ import annotations -from abc import abstractmethod -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Protocol, Union -from polywrap_manifest import AnyWrapManifest +from polywrap_manifest import AnyWrapManifest, DeserializeManifestOptions -from .env import Env from .invoker_client import InvokerClient -from .options.file_options import GetFileOptions -from .options.manifest_options import GetManifestOptions from .uri import Uri -from .uri_package_wrapper import UriPackageOrWrapper from .uri_resolver import UriResolver -class Client(InvokerClient[UriPackageOrWrapper]): +class Client(InvokerClient, Protocol): """Client interface defines core set of functionalities\ for interacting with a wrapper.""" - @abstractmethod def get_interfaces(self) -> Dict[Uri, List[Uri]]: """Get dictionary of interfaces and their implementations. @@ -27,57 +21,64 @@ def get_interfaces(self) -> Dict[Uri, List[Uri]]: Dict[Uri, List[Uri]]: Dictionary of interfaces and their implementations where\ key is interface URI and value is list of implementation uris. """ + ... - @abstractmethod - def get_envs(self) -> Dict[Uri, Env]: + def get_envs(self) -> Dict[Uri, Any]: """Get dictionary of environments. Returns: - Dict[Uri, Env]: Dictionary of environments where key is URI and value is env. + Dict[Uri, Any]: Dictionary of environments where key is URI and value is env. """ + ... - @abstractmethod - def get_env_by_uri(self, uri: Uri) -> Union[Env, None]: + def get_env_by_uri(self, uri: Uri) -> Union[Any, None]: """Get environment by URI. Args: uri (Uri): URI of the Wrapper. Returns: - Union[Env, None]: env if found, otherwise None. + Union[Any, None]: env if found, otherwise None. """ + ... - @abstractmethod def get_uri_resolver(self) -> UriResolver: """Get URI resolver. Returns: - IUriResolver: URI resolver. + UriResolver: URI resolver. """ + ... - @abstractmethod - async def get_file(self, uri: Uri, options: GetFileOptions) -> Union[bytes, str]: + def get_file( + self, uri: Uri, path: str, encoding: Optional[str] = "utf-8" + ) -> Union[bytes, str]: """Get file from URI. Args: uri (Uri): URI of the wrapper. - options (GetFileOptions): Options for getting file from the wrapper. + path (str): Path to the file. + encoding (Optional[str]): Encoding of the file. Returns: Union[bytes, str]]: file contents. """ + ... - @abstractmethod - async def get_manifest( - self, uri: Uri, options: Optional[GetManifestOptions] = None + def get_manifest( + self, uri: Uri, options: Optional[DeserializeManifestOptions] = None ) -> AnyWrapManifest: """Get manifest from URI. Args: uri (Uri): URI of the wrapper. - options (Optional[GetManifestOptions]): \ + options (Optional[DeserializeManifestOptions]): \ Options for getting manifest from the wrapper. Returns: AnyWrapManifest: Manifest of the wrapper. """ + ... + + +__all__ = ["Client"] diff --git a/packages/polywrap-core/polywrap_core/types/config.py b/packages/polywrap-core/polywrap_core/types/config.py index 97afe12c..4b124877 100644 --- a/packages/polywrap-core/polywrap_core/types/config.py +++ b/packages/polywrap-core/polywrap_core/types/config.py @@ -2,9 +2,8 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Dict, List +from typing import Any, Dict, List -from .env import Env from .uri import Uri from .uri_resolver import UriResolver @@ -14,7 +13,7 @@ class ClientConfig: """Client configuration. Attributes: - envs (Dict[Uri, Env]): Dictionary of environments \ + envs (Dict[Uri, Any]): Dictionary of environments \ where key is URI and value is env. interfaces (Dict[Uri, List[Uri]]): Dictionary of interfaces \ and their implementations where key is interface URI \ @@ -22,6 +21,9 @@ class ClientConfig: resolver (UriResolver): URI resolver. """ - envs: Dict[Uri, Env] = field(default_factory=dict) + envs: Dict[Uri, Any] = field(default_factory=dict) interfaces: Dict[Uri, List[Uri]] = field(default_factory=dict) resolver: UriResolver + + +__all__ = ["ClientConfig"] diff --git a/packages/polywrap-core/polywrap_core/types/env.py b/packages/polywrap-core/polywrap_core/types/env.py deleted file mode 100644 index 7949d5eb..00000000 --- a/packages/polywrap-core/polywrap_core/types/env.py +++ /dev/null @@ -1,4 +0,0 @@ -"""This module defines Env type.""" -from typing import Any, Dict - -Env = Dict[str, Any] diff --git a/packages/polywrap-core/polywrap_core/types/errors.py b/packages/polywrap-core/polywrap_core/types/errors.py index f445b191..e89e9ea0 100644 --- a/packages/polywrap-core/polywrap_core/types/errors.py +++ b/packages/polywrap-core/polywrap_core/types/errors.py @@ -1,22 +1,31 @@ """This module contains the core wrap errors.""" +# pylint: disable=too-many-arguments + +from __future__ import annotations import json from textwrap import dedent -from typing import Generic, TypeVar +from typing import Any, Optional, cast + +from polywrap_msgpack import GenericMap, msgpack_decode -from polywrap_msgpack import msgpack_decode +from .invoke_options import InvokeOptions +from .uri import Uri -from .options.invoke_options import InvokeOptions -from .uri_like import UriLike -TUriLike = TypeVar("TUriLike", bound=UriLike) +def _default_encoder(obj: Any) -> Any: + if isinstance(obj, bytes): + return list(obj) + if isinstance(obj, (Uri, GenericMap)): + return repr(cast(Any, obj)) + raise TypeError(f"Object of type '{type(obj).__name__}' is not JSON serializable") class WrapError(Exception): """Base class for all exceptions related to wrappers.""" -class WrapAbortError(Generic[TUriLike], WrapError): +class WrapAbortError(WrapError): """Raises when a wrapper aborts execution. Attributes: @@ -25,35 +34,40 @@ class WrapAbortError(Generic[TUriLike], WrapError): message: The message provided by the wrapper. """ - invoke_options: InvokeOptions[TUriLike] + uri: Uri + method: str message: str + invoke_args: Optional[str] = None + invoke_env: Optional[str] = None - # pylint: disable=too-many-arguments def __init__( self, - invoke_options: InvokeOptions[TUriLike], + invoke_options: InvokeOptions, message: str, ): """Initialize a new instance of WasmAbortError.""" - self.invoke_options = invoke_options + self.uri = invoke_options.uri + self.method = invoke_options.method self.message = message - invoke_args = ( + self.invoke_args = ( json.dumps( msgpack_decode(invoke_options.args) if isinstance(invoke_options.args, bytes) else invoke_options.args, indent=2, + default=_default_encoder, ) if invoke_options.args is not None else None ) - invoke_env = ( + self.invoke_env = ( json.dumps( msgpack_decode(invoke_options.env) if isinstance(invoke_options.env, bytes) else invoke_options.env, indent=2, + default=_default_encoder, ) if invoke_options.env is not None else None @@ -65,15 +79,15 @@ def __init__( WrapAbortError: The following wrapper aborted execution with the given message: URI: {invoke_options.uri} Method: {invoke_options.method} - Args: {invoke_args} - env: {invoke_env} + Args: {self.invoke_args} + env: {self.invoke_env} Message: {message} """ ) ) -class WrapInvocationError(WrapAbortError[TUriLike]): +class WrapInvocationError(WrapAbortError): """Raises when there is an error invoking a wrapper. Attributes: @@ -81,3 +95,39 @@ class WrapInvocationError(WrapAbortError[TUriLike]): that was aborted. message: The message provided by the wrapper. """ + + +class WrapGetImplementationsError(WrapError): + """Raises when there is an error getting implementations of an interface. + + Attributes: + uri (Uri): URI of the interface. + message: The message provided by the wrapper. + """ + + uri: Uri + message: str + + def __init__(self, uri: Uri, message: str): + """Initialize a new instance of WrapGetImplementationsError.""" + self.uri = uri + self.message = message + + super().__init__( + dedent( + f""" + WrapGetImplementationsError: Failed to get implementations of \ + the following interface URI with the given message: + URI: {uri} + Message: {message} + """ + ) + ) + + +__all__ = [ + "WrapError", + "WrapAbortError", + "WrapInvocationError", + "WrapGetImplementationsError", +] diff --git a/packages/polywrap-core/polywrap_core/types/file_reader.py b/packages/polywrap-core/polywrap_core/types/file_reader.py index 6067d0cf..848c69fd 100644 --- a/packages/polywrap-core/polywrap_core/types/file_reader.py +++ b/packages/polywrap-core/polywrap_core/types/file_reader.py @@ -1,14 +1,13 @@ """This module contains file reader interface.""" from __future__ import annotations -from abc import ABC, abstractmethod +from typing import Protocol -class FileReader(ABC): +class FileReader(Protocol): """File reader interface.""" - @abstractmethod - async def read_file(self, file_path: str) -> bytes: + def read_file(self, file_path: str) -> bytes: """Read a file from the given file path. Args: @@ -20,3 +19,7 @@ async def read_file(self, file_path: str) -> bytes: Returns: bytes: The file contents. """ + ... + + +__all__ = ["FileReader"] diff --git a/packages/polywrap-core/polywrap_core/types/invocable.py b/packages/polywrap-core/polywrap_core/types/invocable.py index 12c3d44f..847cfdae 100644 --- a/packages/polywrap-core/polywrap_core/types/invocable.py +++ b/packages/polywrap-core/polywrap_core/types/invocable.py @@ -1,15 +1,14 @@ """This module contains the interface for invoking any invocables.""" +# pylint: disable=too-many-arguments + from __future__ import annotations -from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Any, Generic, Optional, TypeVar - -from .invoker import Invoker -from .options.invoke_options import InvokeOptions -from .uri_like import UriLike +from typing import Any, Optional, Protocol -TUriLike = TypeVar("TUriLike", bound=UriLike) +from .invoker_client import InvokerClient +from .uri import Uri +from .uri_resolution_context import UriResolutionContext @dataclass(slots=True, kw_only=True) @@ -17,29 +16,43 @@ class InvocableResult: """Result of a wrapper invocation. Args: - result (Optional[Any]): Invocation result. The type of this value is \ + result (Any): Invocation result. The type of this value is \ the return type of the method. encoded (Optional[bool]): It will be set true if result is encoded """ - result: Optional[Any] = None + result: Any encoded: Optional[bool] = None -class Invocable(ABC, Generic[TUriLike]): +class Invocable(Protocol): """Invocable interface.""" - @abstractmethod - async def invoke( - self, options: InvokeOptions[TUriLike], invoker: Invoker[TUriLike] + def invoke( + self, + uri: Uri, + method: str, + args: Optional[Any] = None, + env: Optional[Any] = None, + resolution_context: Optional[UriResolutionContext] = None, + client: Optional[InvokerClient] = None, ) -> InvocableResult: """Invoke the Wrapper based on the provided InvokeOptions. Args: - options (InvokeOptions): InvokeOptions for this invocation. - invoker (Invoker): The invoker instance requesting this invocation.\ - This invoker will be used for any subinvocation that may occur. + uri (Uri): Uri of the wrapper + method (str): Method to be executed + args (Optional[Any]) : Arguments for the method, structured as a dictionary + env (Optional[Any]): Override the client's config for all invokes within this invoke. + resolution_context (Optional[UriResolutionContext]): A URI resolution context + client (Optional[InvokerClient]): The invoker client instance requesting\ + this invocation. This invoker client will be used for any subinvocation\ + that may occur. Returns: InvocableResult: Result of the invocation. """ + ... + + +__all__ = ["Invocable", "InvocableResult"] diff --git a/packages/polywrap-core/polywrap_core/types/invoke_args.py b/packages/polywrap-core/polywrap_core/types/invoke_args.py deleted file mode 100644 index d0d074e3..00000000 --- a/packages/polywrap-core/polywrap_core/types/invoke_args.py +++ /dev/null @@ -1,4 +0,0 @@ -"""This module defines InvokeArgs type.""" -from typing import Any, Dict, Union - -InvokeArgs = Union[Dict[str, Any], bytes, None] diff --git a/packages/polywrap-core/polywrap_core/types/invoke_options.py b/packages/polywrap-core/polywrap_core/types/invoke_options.py new file mode 100644 index 00000000..8cab91d6 --- /dev/null +++ b/packages/polywrap-core/polywrap_core/types/invoke_options.py @@ -0,0 +1,34 @@ +"""This module defines the InvokeOptions protocol.""" +from typing import Any, Optional, Protocol + +from .uri import Uri +from .uri_resolution_context import UriResolutionContext + + +class InvokeOptions(Protocol): + """InvokeOptions holds the options for an invocation.""" + + @property + def uri(self) -> Uri: + """The URI of the wrapper.""" + ... + + @property + def method(self) -> str: + """The method to invoke.""" + ... + + @property + def args(self) -> Any: + """The arguments to pass to the method.""" + ... + + @property + def env(self) -> Any: + """The environment variables to set for the invocation.""" + ... + + @property + def resolution_context(self) -> Optional[UriResolutionContext]: + """A URI resolution context.""" + ... diff --git a/packages/polywrap-core/polywrap_core/types/invoker.py b/packages/polywrap-core/polywrap_core/types/invoker.py index b81cb2ca..16083a5c 100644 --- a/packages/polywrap-core/polywrap_core/types/invoker.py +++ b/packages/polywrap-core/polywrap_core/types/invoker.py @@ -1,49 +1,54 @@ """This module contains the interface for invoking any invocables.""" +# pylint: disable=too-many-arguments + from __future__ import annotations -from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import Any, Generic, List, Optional, TypeVar, Union +from typing import Any, List, Optional, Protocol -from .options.invoke_options import InvokeOptions from .uri import Uri -from .uri_like import UriLike - -TUriLike = TypeVar("TUriLike", bound=UriLike) - - -@dataclass(slots=True, kw_only=True) -class InvokerOptions(Generic[TUriLike], InvokeOptions[TUriLike]): - """Options for invoking a wrapper using an invoker. +from .uri_resolution_context import UriResolutionContext - Attributes: - encode_result (Optional[bool]): If true, the result will be encoded. - """ - encode_result: Optional[bool] = False - - -class Invoker(ABC, Generic[TUriLike]): +class Invoker(Protocol): """Invoker interface defines the methods for invoking a wrapper.""" - @abstractmethod - async def invoke(self, options: InvokerOptions[TUriLike]) -> Any: + def invoke( + self, + uri: Uri, + method: str, + args: Optional[Any] = None, + env: Optional[Any] = None, + resolution_context: Optional[UriResolutionContext] = None, + encode_result: Optional[bool] = False, + ) -> Any: """Invoke the Wrapper based on the provided InvokerOptions. Args: - options (InvokerOptions): InvokerOptions for this invocation. + uri (Uri): Uri of the wrapper + method (str): Method to be executed + args (Optional[Any]) : Arguments for the method, structured as a dictionary + env (Optional[Any]): Override the client's config for all invokes within this invoke. + resolution_context (Optional[UriResolutionContext]): A URI resolution context + encode_result (Optional[bool]): If True, the result will be encoded Returns: Any: invocation result. """ + ... - @abstractmethod - def get_implementations(self, uri: Uri) -> Union[List[Uri], None]: + def get_implementations( + self, uri: Uri, apply_resolution: bool = True + ) -> Optional[List[Uri]]: """Get implementations of an interface with its URI. Args: uri (Uri): URI of the interface. + apply_resolution (bool): If True, apply resolution to the URI and interfaces. Returns: - Union[List[Uri], None]: List of implementations or None if not found. + Optional[List[Uri]]: List of implementations or None if not found. """ + ... + + +__all__ = ["Invoker"] diff --git a/packages/polywrap-core/polywrap_core/types/invoker_client.py b/packages/polywrap-core/polywrap_core/types/invoker_client.py index 245e3af4..66d3eaff 100644 --- a/packages/polywrap-core/polywrap_core/types/invoker_client.py +++ b/packages/polywrap-core/polywrap_core/types/invoker_client.py @@ -1,15 +1,15 @@ """This module contains the InvokerClient interface.""" from __future__ import annotations -from typing import Generic, TypeVar +from typing import Protocol from .invoker import Invoker -from .uri_like import UriLike from .uri_resolver_handler import UriResolverHandler -TUriLike = TypeVar("TUriLike", bound=UriLike) - -class InvokerClient(Generic[TUriLike], Invoker[TUriLike], UriResolverHandler[TUriLike]): +class InvokerClient(Invoker, UriResolverHandler, Protocol): """InvokerClient interface defines core set of functionalities\ for resolving and invoking a wrapper.""" + + +__all__ = ["InvokerClient"] diff --git a/packages/polywrap-core/polywrap_core/types/options/__init__.py b/packages/polywrap-core/polywrap_core/types/options/__init__.py deleted file mode 100644 index 1c08b389..00000000 --- a/packages/polywrap-core/polywrap_core/types/options/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""This module contains the options for various client methods.""" -from .file_options import * -from .invoke_options import * -from .manifest_options import * -from .uri_resolver_options import * diff --git a/packages/polywrap-core/polywrap_core/types/options/file_options.py b/packages/polywrap-core/polywrap_core/types/options/file_options.py deleted file mode 100644 index 614fc3e8..00000000 --- a/packages/polywrap-core/polywrap_core/types/options/file_options.py +++ /dev/null @@ -1,18 +0,0 @@ -"""This module contains GetFileOptions type.""" -from __future__ import annotations - -from dataclasses import dataclass -from typing import Optional - - -@dataclass(slots=True, kw_only=True) -class GetFileOptions: - """Options for getting a file from a wrapper. - - Attributes: - path (str): Path to the file. - encoding (Optional[str]): Encoding of the file. - """ - - path: str - encoding: Optional[str] = "utf-8" diff --git a/packages/polywrap-core/polywrap_core/types/options/invoke_options.py b/packages/polywrap-core/polywrap_core/types/options/invoke_options.py deleted file mode 100644 index afe3a1d3..00000000 --- a/packages/polywrap-core/polywrap_core/types/options/invoke_options.py +++ /dev/null @@ -1,32 +0,0 @@ -"""This module contains the interface for invoking any invocables.""" -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Generic, Optional, TypeVar - -from ..env import Env -from ..invoke_args import InvokeArgs -from ..uri import Uri -from ..uri_like import UriLike -from ..uri_resolution_context import IUriResolutionContext - -TUriLike = TypeVar("TUriLike", bound=UriLike) - - -@dataclass(slots=True, kw_only=True) -class InvokeOptions(Generic[TUriLike]): - """Options required for a wrapper invocation. - - Args: - uri (Uri): Uri of the wrapper - method (str): Method to be executed - args (Optional[InvokeArgs]) : Arguments for the method, structured as a dictionary - env (Optional[Env]): Override the client's config for all invokes within this invoke. - resolution_context (Optional[IUriResolutionContext]): A URI resolution context - """ - - uri: Uri - method: str - args: Optional[InvokeArgs] = field(default_factory=dict) - env: Optional[Env] = None - resolution_context: Optional[IUriResolutionContext[TUriLike]] = None diff --git a/packages/polywrap-core/polywrap_core/types/options/manifest_options.py b/packages/polywrap-core/polywrap_core/types/options/manifest_options.py deleted file mode 100644 index 521218df..00000000 --- a/packages/polywrap-core/polywrap_core/types/options/manifest_options.py +++ /dev/null @@ -1,11 +0,0 @@ -"""This module contains GetManifestOptions type.""" -from __future__ import annotations - -from dataclasses import dataclass - -from polywrap_manifest import DeserializeManifestOptions - - -@dataclass(slots=True, kw_only=True) -class GetManifestOptions(DeserializeManifestOptions): - """Options for getting a manifest from a wrapper.""" diff --git a/packages/polywrap-core/polywrap_core/types/options/uri_resolver_options.py b/packages/polywrap-core/polywrap_core/types/options/uri_resolver_options.py deleted file mode 100644 index 9d897d0d..00000000 --- a/packages/polywrap-core/polywrap_core/types/options/uri_resolver_options.py +++ /dev/null @@ -1,24 +0,0 @@ -"""This module contains the TryResolveUriOptions type.""" -from __future__ import annotations - -from dataclasses import dataclass -from typing import Generic, Optional, TypeVar - -from ..uri import Uri -from ..uri_like import UriLike -from ..uri_resolution_context import IUriResolutionContext - -TUriLike = TypeVar("TUriLike", bound=UriLike) - - -@dataclass(slots=True, kw_only=True) -class TryResolveUriOptions(Generic[TUriLike]): - """Options for resolving a uri. - - Args: - uri (Uri): Uri of the wrapper to resolve. - resolution_context (Optional[IUriResolutionContext]): A URI resolution context - """ - - uri: Uri - resolution_context: Optional[IUriResolutionContext[TUriLike]] = None diff --git a/packages/polywrap-core/polywrap_core/types/uri.py b/packages/polywrap-core/polywrap_core/types/uri.py index f6390bf2..425b7a9a 100644 --- a/packages/polywrap-core/polywrap_core/types/uri.py +++ b/packages/polywrap-core/polywrap_core/types/uri.py @@ -2,12 +2,12 @@ from __future__ import annotations import re +from functools import total_ordering from typing import Union -from .uri_like import UriLike - -class Uri(UriLike): +@total_ordering +class Uri: """Defines a wrapper URI and provides utilities for parsing and validating them. wrapper URIs are used to identify and resolve Polywrap wrappers. They are \ @@ -54,6 +54,7 @@ class Uri(UriLike): r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?" ) # https://www.rfc-editor.org/rfc/rfc3986#appendix-B + scheme = "wrap" _authority: str _path: str @@ -61,8 +62,8 @@ def __init__(self, authority: str, path: str): """Initialize a new instance of a wrapper URI. Args: - authority: The authority of the URI. - path: The path of the URI. + authority (str): The authority of the URI. + path (str): The path of the URI. """ self._authority = authority self._path = path @@ -150,3 +151,29 @@ def from_str(cls, uri: str) -> Uri: raise ValueError("The provided URI has an invalid path") return cls(authority, path) + + def __str__(self) -> str: + """Return the URI as a string.""" + return self.uri + + def __repr__(self) -> str: + """Return the string URI representation.""" + return f'Uri("{self._authority}", "{self._path}")' + + def __hash__(self) -> int: + """Return the hash of the URI.""" + return hash(self.uri) + + def __eq__(self, obj: object) -> bool: + """Return true if the provided object is a Uri and has the same URI.""" + return self.uri == obj.uri if isinstance(obj, Uri) else False + + def __lt__(self, uri: object) -> bool: + """Return true if the provided Uri has a URI that is lexicographically\ + less than this Uri.""" + if not isinstance(uri, Uri): + raise TypeError(f"Cannot compare Uri to {type(uri)}") + return self.uri < uri.uri + + +__all__ = ["Uri"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_like.py b/packages/polywrap-core/polywrap_core/types/uri_like.py deleted file mode 100644 index 39014221..00000000 --- a/packages/polywrap-core/polywrap_core/types/uri_like.py +++ /dev/null @@ -1,64 +0,0 @@ -"""This module contains the utility for sanitizing and parsing Wrapper URIs.""" -from __future__ import annotations - -from abc import ABC, abstractmethod -from functools import total_ordering - - -@total_ordering -class UriLike(ABC): - """Defines the interface for a URI-like object. - - Examples: - - Uri - - UriWrapper - - UriPackage - """ - - @property - def scheme(self) -> str: - """Return the scheme of the URI.""" - return "wrap" - - @property - @abstractmethod - def authority(self) -> str: - """Return the authority of the URI.""" - - @property - @abstractmethod - def path(self) -> str: - """Return the path of the URI.""" - - @property - @abstractmethod - def uri(self) -> str: - """Return the URI as a string.""" - - @staticmethod - @abstractmethod - def is_canonical_uri(uri: str) -> bool: - """Return true if the provided URI is canonical.""" - - def __str__(self) -> str: - """Return the URI as a string.""" - return self.uri - - def __repr__(self) -> str: - """Return the string URI representation.""" - return f"Uri({self.uri})" - - def __hash__(self) -> int: - """Return the hash of the URI.""" - return hash(self.uri) - - def __eq__(self, obj: object) -> bool: - """Return true if the provided object is a Uri and has the same URI.""" - return self.uri == obj.uri if isinstance(obj, UriLike) else False - - def __lt__(self, uri: object) -> bool: - """Return true if the provided Uri has a URI that is lexicographically\ - less than this Uri.""" - if not isinstance(uri, UriLike): - raise TypeError(f"Cannot compare Uri to {type(uri)}") - return self.uri < uri.uri diff --git a/packages/polywrap-core/polywrap_core/types/uri_package.py b/packages/polywrap-core/polywrap_core/types/uri_package.py index 8d84f5d3..63b61948 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_package.py +++ b/packages/polywrap-core/polywrap_core/types/uri_package.py @@ -1,35 +1,23 @@ """This module contains the UriPackage type.""" from __future__ import annotations -from typing import Generic, TypeVar +from dataclasses import dataclass from .uri import Uri -from .uri_like import UriLike from .wrap_package import WrapPackage -TUriLike = TypeVar("TUriLike", bound=UriLike) - -class UriPackage(Generic[TUriLike], Uri): - """UriPackage is a dataclass that contains a URI and a wrap package. +@dataclass(slots=True, kw_only=True) +class UriPackage: + """UriPackage is a dataclass that contains a URI and a package. Attributes: - package (WrapPackage): The wrap package. + uri (Uri): The URI. + package (Package): The package. """ - _package: WrapPackage[TUriLike] - - def __init__(self, uri: Uri, package: WrapPackage[TUriLike]) -> None: - """Initialize a new instance of UriPackage. + uri: Uri + package: WrapPackage - Args: - uri (Uri): The URI. - package (WrapPackage): The wrap package. - """ - super().__init__(uri.authority, uri.path) - self._package = package - @property - def package(self) -> WrapPackage[TUriLike]: - """Return the wrap package.""" - return self._package +__all__ = ["UriPackage"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_package_wrapper.py b/packages/polywrap-core/polywrap_core/types/uri_package_wrapper.py index 8351aac8..a4c3e5da 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_package_wrapper.py +++ b/packages/polywrap-core/polywrap_core/types/uri_package_wrapper.py @@ -7,6 +7,6 @@ from .uri_package import UriPackage from .uri_wrapper import UriWrapper -UriPackageOrWrapper = Union[ - Uri, UriWrapper["UriPackageOrWrapper"], UriPackage["UriPackageOrWrapper"] -] +UriPackageOrWrapper = Union[Uri, UriWrapper, UriPackage] + +__all__ = ["UriPackageOrWrapper"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_resolution_context.py b/packages/polywrap-core/polywrap_core/types/uri_resolution_context.py index 3909a36e..4b852595 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_resolution_context.py +++ b/packages/polywrap-core/polywrap_core/types/uri_resolution_context.py @@ -1,20 +1,48 @@ -"""This module contains the interface for a URI resolution context.""" -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Generic, List, TypeVar +"""This module contains implementation of IUriResolutionContext interface.""" +from typing import List, Optional, Set from .uri import Uri -from .uri_like import UriLike -from .uri_resolution_step import IUriResolutionStep +from .uri_resolution_step import UriResolutionStep + + +class UriResolutionContext: + """Represents the context of a uri resolution. -TUriLike = TypeVar("TUriLike", bound=UriLike) + Attributes: + resolving_uri_set (Set[Uri]): A set of uris that\ + are currently being resolved. + resolution_path (List[Uri]): A list of uris in the order that\ + they are being resolved. + history (List[UriResolutionStep]): A list of steps \ + that have been taken to resolve the uri. + """ + resolving_uri_set: Set[Uri] + resolution_path: List[Uri] + history: List[UriResolutionStep] -class IUriResolutionContext(ABC, Generic[TUriLike]): - """Defines the interface for a URI resolution context.""" + __slots__ = ("resolving_uri_set", "resolution_path", "history") + + def __init__( + self, + resolving_uri_set: Optional[Set[Uri]] = None, + resolution_path: Optional[List[Uri]] = None, + history: Optional[List[UriResolutionStep]] = None, + ): + """Initialize a new instance of UriResolutionContext. + + Args: + resolving_uri_set (Optional[Set[Uri]]): A set of uris that\ + are currently being resolved. + resolution_path (Optional[List[Uri]]): A list of uris in the order that\ + they are being resolved. + history (Optional[List[UriResolutionStep]]): A list of steps \ + that have been taken to resolve the uri. + """ + self.resolving_uri_set = resolving_uri_set or set() + self.resolution_path = resolution_path or [] + self.history = history or [] - @abstractmethod def is_resolving(self, uri: Uri) -> bool: """Check if the given uri is currently being resolved. @@ -24,8 +52,8 @@ def is_resolving(self, uri: Uri) -> bool: Returns: bool: True if the uri is currently being resolved, otherwise False. """ + return uri in self.resolving_uri_set - @abstractmethod def start_resolving(self, uri: Uri) -> None: """Start resolving the given uri. @@ -34,8 +62,9 @@ def start_resolving(self, uri: Uri) -> None: Returns: None """ + self.resolving_uri_set.add(uri) + self.resolution_path.append(uri) - @abstractmethod def stop_resolving(self, uri: Uri) -> None: """Stop resolving the given uri. @@ -44,45 +73,54 @@ def stop_resolving(self, uri: Uri) -> None: Returns: None """ + self.resolving_uri_set.remove(uri) - @abstractmethod - def track_step(self, step: IUriResolutionStep[TUriLike]) -> None: + def track_step(self, step: UriResolutionStep) -> None: """Track the given step in the resolution history. Args: - step (IUriResolutionStep): The step to track. + step (UriResolutionStep): The step to track. Returns: None """ + self.history.append(step) - @abstractmethod - def get_history(self) -> List[IUriResolutionStep[TUriLike]]: + def get_history(self) -> List[UriResolutionStep]: """Get the resolution history. Returns: - List[IUriResolutionStep]: The resolution history. + List[UriResolutionStep]: The resolution history. """ + return self.history - @abstractmethod def get_resolution_path(self) -> List[Uri]: """Get the resolution path. Returns: List[Uri]: The ordered list of URI resolution path. """ + return self.resolution_path - @abstractmethod - def create_sub_history_context(self) -> "IUriResolutionContext[TUriLike]": + def create_sub_history_context(self) -> "UriResolutionContext": """Create a new sub context that shares the same resolution path. Returns: - IUriResolutionContext: The new context. + UriResolutionContext: The new context. """ + return UriResolutionContext( + resolving_uri_set=self.resolving_uri_set, + resolution_path=self.resolution_path, + ) - @abstractmethod - def create_sub_context(self) -> "IUriResolutionContext[TUriLike]": + def create_sub_context(self) -> "UriResolutionContext": """Create a new sub context that shares the same resolution history. Returns: - IUriResolutionContext: The new context. + UriResolutionContext: The new context. """ + return UriResolutionContext( + resolving_uri_set=self.resolving_uri_set, history=self.history + ) + + +__all__ = ["UriResolutionContext"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_resolution_step.py b/packages/polywrap-core/polywrap_core/types/uri_resolution_step.py index ed75a331..4ababc33 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_resolution_step.py +++ b/packages/polywrap-core/polywrap_core/types/uri_resolution_step.py @@ -1,27 +1,28 @@ -"""This module contains the uri resolution step interface.""" +"""This module contains implementation of IUriResolutionStep interface.""" from __future__ import annotations from dataclasses import dataclass -from typing import Generic, List, Optional, TypeVar +from typing import Any, List, Optional from .uri import Uri -from .uri_like import UriLike - -TUriLike = TypeVar("TUriLike", bound=UriLike) @dataclass(slots=True, kw_only=True) -class IUriResolutionStep(Generic[TUriLike]): +class UriResolutionStep: """Represents a single step in the resolution of a uri. Attributes: source_uri (Uri): The uri that was resolved. - result (T): The result of the resolution. must be a UriLike. - description: A description of the resolution step. - sub_history: A list of sub steps that were taken to resolve the uri. + result (Any): The result of the resolution. + description (Optional[str]): A description of the resolution step. + sub_history (Optional[List[UriResolutionStep]]): A list of sub steps\ + that were taken to resolve the uri. """ source_uri: Uri - result: TUriLike + result: Any description: Optional[str] = None - sub_history: Optional[List["IUriResolutionStep[TUriLike]"]] = None + sub_history: Optional[List[UriResolutionStep]] = None + + +__all__ = ["UriResolutionStep"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_resolver.py b/packages/polywrap-core/polywrap_core/types/uri_resolver.py index 66463d1c..0de1f60a 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_resolver.py +++ b/packages/polywrap-core/polywrap_core/types/uri_resolver.py @@ -1,31 +1,36 @@ """This module contains the uri resolver interface.""" from __future__ import annotations -from abc import ABC, abstractmethod +from typing import Protocol from .invoker_client import InvokerClient from .uri import Uri from .uri_package_wrapper import UriPackageOrWrapper -from .uri_resolution_context import IUriResolutionContext +from .uri_resolution_context import UriResolutionContext -class UriResolver(ABC): +class UriResolver(Protocol): """Defines interface for wrapper uri resolver.""" - @abstractmethod - async def try_resolve_uri( + def try_resolve_uri( self, uri: Uri, - client: InvokerClient[UriPackageOrWrapper], - resolution_context: IUriResolutionContext[UriPackageOrWrapper], + client: InvokerClient, + resolution_context: UriResolutionContext, ) -> UriPackageOrWrapper: """Try to resolve a uri. Args: - uri: The uri to resolve. - client: The minimal invoker client to use for resolving the uri. - resolution_context: The context for resolving the uri. + uri (Uri): The uri to resolve. + client (InvokerClient): The minimal invoker client \ + to use for resolving the uri. + resolution_context (UriResolutionContext): The context \ + for resolving the uri. Returns: UriPackageOrWrapper: result of the URI resolution. """ + ... + + +__all__ = ["UriResolver"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_resolver_handler.py b/packages/polywrap-core/polywrap_core/types/uri_resolver_handler.py index 3c56687a..ca1140b5 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_resolver_handler.py +++ b/packages/polywrap-core/polywrap_core/types/uri_resolver_handler.py @@ -1,27 +1,29 @@ """This module contains uri resolver handler interface.""" from __future__ import annotations -from abc import ABC, abstractmethod -from typing import Generic, TypeVar +from typing import Any, Optional, Protocol -from .options.uri_resolver_options import TryResolveUriOptions -from .uri_like import UriLike +from .uri import Uri +from .uri_resolution_context import UriResolutionContext -TUriLike = TypeVar("TUriLike", bound=UriLike) - -class UriResolverHandler(ABC, Generic[TUriLike]): +class UriResolverHandler(Protocol): """Uri resolver handler interface.""" - @abstractmethod - async def try_resolve_uri( - self, options: TryResolveUriOptions[TUriLike] - ) -> TUriLike: + def try_resolve_uri( + self, uri: Uri, resolution_context: Optional[UriResolutionContext] = None + ) -> Any: """Try to resolve a uri. Args: - options: The options for resolving the uri. + uri (Uri): Uri of the wrapper to resolve. + resolution_context (Optional[IUriResolutionContext]):\ + A URI resolution context Returns: - T: result of the URI resolution. Must be a UriLike. + Any: result of the URI resolution. """ + ... + + +__all__ = ["UriResolverHandler"] diff --git a/packages/polywrap-core/polywrap_core/types/uri_wrapper.py b/packages/polywrap-core/polywrap_core/types/uri_wrapper.py index 56749816..8930220e 100644 --- a/packages/polywrap-core/polywrap_core/types/uri_wrapper.py +++ b/packages/polywrap-core/polywrap_core/types/uri_wrapper.py @@ -1,35 +1,23 @@ """This module contains the UriWrapper type.""" from __future__ import annotations -from typing import Generic, TypeVar +from dataclasses import dataclass from .uri import Uri -from .uri_like import UriLike from .wrapper import Wrapper -TUriLike = TypeVar("TUriLike", bound=UriLike) - -class UriWrapper(Generic[TUriLike], Uri): +@dataclass(slots=True, kw_only=True) +class UriWrapper: """UriWrapper is a dataclass that contains a URI and a wrapper. Attributes: - wrapper: The wrapper. + uri (Uri): The URI. + wrapper (Wrapper): The wrapper. """ - _wrapper: Wrapper[TUriLike] - - def __init__(self, uri: Uri, wrapper: Wrapper[TUriLike]) -> None: - """Initialize a new instance of UriWrapper. + uri: Uri + wrapper: Wrapper - Args: - uri: The URI. - wrapper: The wrapper. - """ - super().__init__(uri.authority, uri.path) - self._wrapper = wrapper - @property - def wrapper(self) -> Wrapper[TUriLike]: - """Return the wrapper.""" - return self._wrapper +__all__ = ["UriWrapper"] diff --git a/packages/polywrap-core/polywrap_core/types/wrap_package.py b/packages/polywrap-core/polywrap_core/types/wrap_package.py index 4ce160d1..e9f4b30e 100644 --- a/packages/polywrap-core/polywrap_core/types/wrap_package.py +++ b/packages/polywrap-core/polywrap_core/types/wrap_package.py @@ -1,36 +1,36 @@ """This module contains the IWrapPackage interface.""" -from abc import ABC, abstractmethod -from typing import Generic, Optional, TypeVar +from __future__ import annotations -from polywrap_manifest import AnyWrapManifest +from typing import Optional, Protocol -from .options import GetManifestOptions -from .uri_like import UriLike -from .wrapper import Wrapper +from polywrap_manifest import AnyWrapManifest, DeserializeManifestOptions -TUriLike = TypeVar("TUriLike", bound=UriLike) +from .wrapper import Wrapper -class WrapPackage(ABC, Generic[TUriLike]): +class WrapPackage(Protocol): """Wrapper package interface.""" - @abstractmethod - async def create_wrapper(self) -> Wrapper[TUriLike]: + def create_wrapper(self) -> Wrapper: """Create a new wrapper instance from the wrapper package. Returns: Wrapper: The newly created wrapper instance. """ + ... - @abstractmethod - async def get_manifest( - self, options: Optional[GetManifestOptions] = None + def get_manifest( + self, options: Optional[DeserializeManifestOptions] = None ) -> AnyWrapManifest: """Get the manifest from the wrapper package. Args: - options: The options for getting the manifest. + options (DeserializeManifestOptions): The options for getting the manifest. Returns: AnyWrapManifest: The manifest of the wrapper. """ + ... + + +__all__ = ["WrapPackage"] diff --git a/packages/polywrap-core/polywrap_core/types/wrapper.py b/packages/polywrap-core/polywrap_core/types/wrapper.py index d92fe87c..303893e6 100644 --- a/packages/polywrap-core/polywrap_core/types/wrapper.py +++ b/packages/polywrap-core/polywrap_core/types/wrapper.py @@ -1,52 +1,37 @@ """This module contains the Wrapper interface.""" -from abc import abstractmethod -from typing import Any, Dict, Generic, TypeVar, Union +from __future__ import annotations + +from typing import Optional, Protocol, Union from polywrap_manifest import AnyWrapManifest from .invocable import Invocable -from .invoker import InvokeOptions, Invoker -from .options import GetFileOptions -from .uri_like import UriLike - -TUriLike = TypeVar("TUriLike", bound=UriLike) -class Wrapper(Generic[TUriLike], Invocable[TUriLike]): +class Wrapper(Invocable, Protocol): """Defines the interface for a wrapper.""" - @abstractmethod - async def invoke( - self, options: InvokeOptions[TUriLike], invoker: Invoker[TUriLike] - ) -> Any: - """Invoke the wrapper. - - Args: - options: The options for invoking the wrapper. - invoker: The invoker to use for invoking the wrapper. - - Returns: - Any: The result of the wrapper invocation. - """ - - @abstractmethod - async def get_file(self, options: GetFileOptions) -> Union[str, bytes]: + def get_file( + self, path: str, encoding: Optional[str] = "utf-8" + ) -> Union[str, bytes]: """Get a file from the wrapper. Args: - options: The options for getting the file. + path (str): Path to the file. + encoding (Optional[str]): Encoding of the file. Returns: Union[str, bytes]: The file contents """ + ... - @abstractmethod def get_manifest(self) -> AnyWrapManifest: """Get the manifest of the wrapper. Returns: AnyWrapManifest: The manifest of the wrapper. """ + ... -WrapperCache = Dict[str, Wrapper[TUriLike]] +__all__ = ["Wrapper"] diff --git a/packages/polywrap-core/polywrap_core/utils/__init__.py b/packages/polywrap-core/polywrap_core/utils/__init__.py index 0d6a3b67..abd549d8 100644 --- a/packages/polywrap-core/polywrap_core/utils/__init__.py +++ b/packages/polywrap-core/polywrap_core/utils/__init__.py @@ -1,3 +1,4 @@ -"""This module contains the core utility functions.""" -from .instance_of import * -from .maybe_async import * +"""This package contains the utilities used by the polywrap-uri-resolvers package.""" +from .build_clean_uri_history import * +from .get_env_from_resolution_path import * +from .get_implementations import * diff --git a/packages/polywrap-core/polywrap_core/utils/build_clean_uri_history.py b/packages/polywrap-core/polywrap_core/utils/build_clean_uri_history.py new file mode 100644 index 00000000..a22dd0f1 --- /dev/null +++ b/packages/polywrap-core/polywrap_core/utils/build_clean_uri_history.py @@ -0,0 +1,73 @@ +"""This module contains an utility function for building a clean history of URI resolution steps.""" +from typing import List, Optional, Union + +from ..types import UriPackage, UriResolutionStep, UriWrapper + +CleanResolutionStep = List[Union[str, "CleanResolutionStep"]] + + +def build_clean_uri_history( + history: List[UriResolutionStep], depth: Optional[int] = None +) -> CleanResolutionStep: + """Build a clean history of the URI resolution steps. + + Args: + history: A list of URI resolution steps. + depth: The depth of the history to build. + + Returns: + CleanResolutionStep: A clean history of the URI resolution steps. + """ + clean_history: CleanResolutionStep = [] + + if depth is not None: + depth -= 1 + + if not history: + return clean_history + + for step in history: + clean_history.append(_build_clean_history_step(step)) + + if ( + not step.sub_history + or len(step.sub_history) == 0 + or (depth is not None and depth < 0) + ): + continue + + sub_history = build_clean_uri_history(step.sub_history, depth) + if len(sub_history) > 0: + clean_history.append(sub_history) + + return clean_history + + +def _build_clean_history_step(step: UriResolutionStep) -> str: + uri_package_or_wrapper = step.result + + match uri_package_or_wrapper: + case UriPackage(uri=uri): + return ( + f"{step.source_uri} => {step.description} => package ({uri})" + if step.description + else f"{step.source_uri} => package ({uri})" + ) + case UriWrapper(uri=uri): + return ( + f"{step.source_uri} => {step.description} => wrapper ({uri})" + if step.description + else f"{step.source_uri} => wrapper ({uri})" + ) + case uri: + if step.source_uri == uri: + return ( + f"{step.source_uri} => {step.description}" + if step.description + else f"{step.source_uri}" + ) + return ( + f"{step.source_uri} => {step.description} => uri ({uri})" + if step.description + else f"{step.source_uri} => uri ({uri})" + ) diff --git a/packages/polywrap-core/polywrap_core/utils/get_env_from_resolution_path.py b/packages/polywrap-core/polywrap_core/utils/get_env_from_resolution_path.py new file mode 100644 index 00000000..33bc4ac2 --- /dev/null +++ b/packages/polywrap-core/polywrap_core/utils/get_env_from_resolution_path.py @@ -0,0 +1,22 @@ +"""This module contains the utility function for getting the env from the URI history.""" +from typing import Any, List, Union + +from ..types import Client, Uri + + +def get_env_from_resolution_path( + uri_history: List[Uri], client: Client +) -> Union[Any, None]: + """Get environment variable from URI resolution history. + + Args: + uri_history: List of URIs from the URI resolution history + client: Polywrap client instance to use for getting the env by URI + + Returns: + env if found, None otherwise + """ + for uri in uri_history: + if env := client.get_env_by_uri(uri): + return env + return None diff --git a/packages/polywrap-core/polywrap_core/utils/get_implementations.py b/packages/polywrap-core/polywrap_core/utils/get_implementations.py new file mode 100644 index 00000000..e8657129 --- /dev/null +++ b/packages/polywrap-core/polywrap_core/utils/get_implementations.py @@ -0,0 +1,49 @@ +"""This module contains the get_implementations utility.""" +from typing import Dict, List, Optional, Set + +from ..types import InvokerClient, Uri, UriResolutionContext +from ..types.errors import WrapGetImplementationsError + + +def _get_final_uri( + uri: Uri, + client: Optional[InvokerClient] = None, + resolution_context: Optional[UriResolutionContext] = None, +) -> Uri: + if client: + try: + return client.try_resolve_uri(uri, resolution_context) + except Exception as e: + raise WrapGetImplementationsError(uri, "Failed to resolve redirects") from e + return uri + + +def get_implementations( + interface_uri: Uri, + interfaces: Dict[Uri, List[Uri]], + client: Optional[InvokerClient] = None, + resolution_context: Optional[UriResolutionContext] = None, +) -> Optional[List[Uri]]: + """Get implementations of an interface with its URI. + + Args: + interface_uri (Uri): URI of the interface. + interfaces (Dict[Uri, List[Uri]]): Dictionary of interfaces and their implementations. + client (Optional[InvokerClient]): The client to use for resolving the URI. + resolution_context (Optional[UriResolutionContext]): The resolution context to use. + + Returns: + Optional[List[Uri]]: List of implementations or None if not found. + """ + final_interface_uri = _get_final_uri(interface_uri, client, resolution_context) + final_implementations: Set[Uri] = set() + + for interface in interfaces: + final_current_interface_uri = _get_final_uri( + interface, client, resolution_context + ) + if final_current_interface_uri == final_interface_uri: + impls = set(interfaces.get(interface, [])) + final_implementations = final_implementations.union(impls) + + return list(final_implementations) if final_implementations else None diff --git a/packages/polywrap-core/polywrap_core/utils/instance_of.py b/packages/polywrap-core/polywrap_core/utils/instance_of.py deleted file mode 100644 index 3772179d..00000000 --- a/packages/polywrap-core/polywrap_core/utils/instance_of.py +++ /dev/null @@ -1,16 +0,0 @@ -"""This module contains the instance_of utility function.""" -import inspect -from typing import Any - - -def instance_of(obj: Any, cls: Any): - """Check if an object is an instance of a class or any of its parent classes. - - Args: - obj (Any): any object instance - cls (Any): class to check against - - Returns: - bool: True if obj is an instance of cls or any of its parent classes, False otherwise - """ - return cls in inspect.getmro(obj.__class__) diff --git a/packages/polywrap-core/polywrap_core/utils/maybe_async.py b/packages/polywrap-core/polywrap_core/utils/maybe_async.py deleted file mode 100644 index 913182f2..00000000 --- a/packages/polywrap-core/polywrap_core/utils/maybe_async.py +++ /dev/null @@ -1,18 +0,0 @@ -"""This module contains the utility function for executing a function that may be async.""" -from __future__ import annotations - -import inspect -from typing import Any, Awaitable, Callable, Optional, Union - - -def is_coroutine(test: Optional[Union[Awaitable[Any], Any]] = None) -> bool: - """Check if the given object is a coroutine.""" - return test is not None and inspect.iscoroutine(test) - - -async def execute_maybe_async_function(func: Callable[..., Any], *args: Any) -> Any: - """Execute a function that may be async.""" - result = func(*args) - if is_coroutine(result): - result = await result - return result diff --git a/packages/polywrap-core/pyproject.toml b/packages/polywrap-core/pyproject.toml index dc73c351..5097c49e 100644 --- a/packages/polywrap-core/pyproject.toml +++ b/packages/polywrap-core/pyproject.toml @@ -12,9 +12,10 @@ authors = ["Cesar ", "Niraj "] python = "^3.10" polywrap-msgpack = {path = "../polywrap-msgpack", develop = true} polywrap-manifest = {path = "../polywrap-manifest", develop = true} -[tool.poetry.dev-dependencies] + +[tool.poetry.group.dev.dependencies] +pycln = "^2.1.3" pytest = "^7.1.2" -pytest-asyncio = "^0.19.0" pylint = "^2.15.4" black = "^22.10.0" bandit = { version = "^1.7.4", extras = ["toml"]} @@ -24,13 +25,6 @@ isort = "^5.10.1" pyright = "^1.1.275" pydocstyle = "^6.1.1" -[tool.poetry.group.temp.dependencies] -pydeps = "^1.11.1" - - -[tool.poetry.group.dev.dependencies] -pycln = "^2.1.3" - [tool.bandit] exclude_dirs = ["tests"] @@ -42,7 +36,6 @@ typeCheckingMode = "strict" reportShadowedImports = false [tool.pytest.ini_options] -asyncio_mode = "auto" testpaths = [ "tests" ] @@ -52,6 +45,7 @@ disable = [ "invalid-name", "too-many-return-statements", "too-few-public-methods", + "unnecessary-ellipsis" ] ignore = [ "tests/" diff --git a/packages/polywrap-core/tests/test_build_clean_uri_history.py b/packages/polywrap-core/tests/test_build_clean_uri_history.py new file mode 100644 index 00000000..e5da523d --- /dev/null +++ b/packages/polywrap-core/tests/test_build_clean_uri_history.py @@ -0,0 +1,46 @@ +from polywrap_core import ( + CleanResolutionStep, + Uri, + build_clean_uri_history, + UriResolutionStep, +) +import pytest + + +@pytest.fixture +def history() -> list[UriResolutionStep]: + return [ + UriResolutionStep( + source_uri=Uri.from_str("test/1"), + result=Uri.from_str("test/2"), + description="AggreagatorResolver", + sub_history=[ + UriResolutionStep( + source_uri=Uri.from_str("test/1"), + result=Uri.from_str("test/2"), + description="ExtensionRedirectResolver", + ), + ], + ), + UriResolutionStep( + source_uri=Uri.from_str("test/2"), + result=Uri.from_str("test/3"), + description="SimpleRedirectResolver", + ), + ] + + +@pytest.fixture +def expected() -> CleanResolutionStep: + return [ + "wrap://test/1 => AggreagatorResolver => uri (wrap://test/2)", + ["wrap://test/1 => ExtensionRedirectResolver => uri (wrap://test/2)"], + "wrap://test/2 => SimpleRedirectResolver => uri (wrap://test/3)", + ] + + +def test_build_clean_uri_history( + history: list[UriResolutionStep], expected: CleanResolutionStep +): + print(build_clean_uri_history(history)) + assert build_clean_uri_history(history) == expected diff --git a/packages/polywrap-core/tests/test_env_from_resolution_path.py b/packages/polywrap-core/tests/test_env_from_resolution_path.py new file mode 100644 index 00000000..5c832372 --- /dev/null +++ b/packages/polywrap-core/tests/test_env_from_resolution_path.py @@ -0,0 +1,40 @@ +from typing import Any +from polywrap_core import ( + Client, + Uri, + get_env_from_resolution_path, +) +import pytest + + +@pytest.fixture +def resolution_path() -> list[Any]: + return [ + Uri.from_str("test/1"), + Uri.from_str("test/2"), + Uri.from_str("test/3"), + ] + + +@pytest.fixture +def client() -> Any: + class MockClient: + def get_env_by_uri(self, uri: Uri) -> Any: + if uri.uri == "wrap://test/3": + return { + "arg1": "arg1", + "arg2": "arg2", + } + + return MockClient() + + +def test_get_env_from_resolution_path(resolution_path: list[Any], client: Client): + assert get_env_from_resolution_path(resolution_path, client) == { + "arg1": "arg1", + "arg2": "arg2", + } + + +def test_get_env_from_resolution_path_empty(client: Client): + assert get_env_from_resolution_path([], client) is None diff --git a/packages/polywrap-core/tests/test_get_implementations.py b/packages/polywrap-core/tests/test_get_implementations.py new file mode 100644 index 00000000..b8e3cd84 --- /dev/null +++ b/packages/polywrap-core/tests/test_get_implementations.py @@ -0,0 +1,79 @@ +from polywrap_core import ( + Any, + Client, + Uri, + get_implementations, +) +import pytest + +interface_1 = Uri.from_str("wrap://ens/interface-1.eth") +interface_2 = Uri.from_str("wrap://ens/interface-2.eth") +interface_3 = Uri.from_str("wrap://ens/interface-3.eth") + +implementation_1 = Uri.from_str("wrap://ens/implementation-1.eth") +implementation_2 = Uri.from_str("wrap://ens/implementation-2.eth") +implementation_3 = Uri.from_str("wrap://ens/implementation-3.eth") + + +redirects = { + interface_1: interface_2, + implementation_1: implementation_2, + implementation_2: implementation_3, +} + +interfaces = { + interface_1: [implementation_1, implementation_2], + interface_2: [implementation_3], + interface_3: [implementation_3], +} + + +@pytest.fixture +def client() -> Any: + class MockClient: + def try_resolve_uri(self, uri: Uri, *args: Any) -> Uri: + return redirects.get(uri, uri) + + return MockClient() + + +def test_get_implementations_1(client: Client): + result = get_implementations(interface_1, interfaces, client) + + assert result + assert set(result) == { + implementation_1, + implementation_2, + implementation_3, + } + + +def test_get_implementations_2(client: Client): + result = get_implementations(interface_2, interfaces, client) + + assert result + assert set(result) == { + implementation_1, + implementation_2, + implementation_3, + } + + +def test_get_implementations_3(client: Client): + result = get_implementations(interface_3, interfaces, client) + + assert result + assert set(result) == { + implementation_3, + } + + +def test_implementations_not_redirected(client: Client): + result = get_implementations(interface_1, { + interface_1: [implementation_1], + }, client) + + assert result + assert set(result) == { + implementation_1, + } \ No newline at end of file diff --git a/packages/polywrap-core/tests/test_maybe_async.py b/packages/polywrap-core/tests/test_maybe_async.py deleted file mode 100644 index 6e3323f9..00000000 --- a/packages/polywrap-core/tests/test_maybe_async.py +++ /dev/null @@ -1,27 +0,0 @@ -import inspect - -import pytest - -from polywrap_core import execute_maybe_async_function, is_coroutine - - -@pytest.mark.asyncio -async def test_sanity(): - async def coroutine(): - pass - - def test_function(): - pass - - async def test_function_return_promise(): - pass - - test_coroutine_resp = coroutine() - test_function_resp = execute_maybe_async_function(test_function) - test_function_return_promise_resp = execute_maybe_async_function(test_function_return_promise) - assert is_coroutine(test_coroutine_resp) - assert inspect.iscoroutine(test_function_resp) - assert inspect.iscoroutine(test_function_return_promise_resp) - await test_coroutine_resp - await test_function_resp - await test_function_return_promise_resp diff --git a/packages/polywrap-manifest/polywrap_manifest/deserialize.py b/packages/polywrap-manifest/polywrap_manifest/deserialize.py index 1f2f169e..d8996505 100644 --- a/packages/polywrap-manifest/polywrap_manifest/deserialize.py +++ b/packages/polywrap-manifest/polywrap_manifest/deserialize.py @@ -53,3 +53,6 @@ def deserialize_wrap_manifest( raise NotImplementedError( f"Version {manifest_version.value} is not implemented" ) + + +__all__ = ["deserialize_wrap_manifest"] diff --git a/packages/polywrap-manifest/polywrap_manifest/errors.py b/packages/polywrap-manifest/polywrap_manifest/errors.py index 0bdbff49..19015dde 100644 --- a/packages/polywrap-manifest/polywrap_manifest/errors.py +++ b/packages/polywrap-manifest/polywrap_manifest/errors.py @@ -7,3 +7,6 @@ class ManifestError(Exception): class DeserializeManifestError(ManifestError): """Raised when a manifest cannot be deserialized.""" + + +__all__ = ["ManifestError", "DeserializeManifestError"] diff --git a/packages/polywrap-manifest/polywrap_manifest/manifest.py b/packages/polywrap-manifest/polywrap_manifest/manifest.py index 29268c31..d0be947c 100644 --- a/packages/polywrap-manifest/polywrap_manifest/manifest.py +++ b/packages/polywrap-manifest/polywrap_manifest/manifest.py @@ -22,17 +22,6 @@ class DeserializeManifestOptions: no_validate: Optional[bool] = None -@dataclass(slots=True, kw_only=True) -class SerializeManifestOptions: - """Options for serializing a manifest to msgpack encoded bytes. - - Attributes: - no_validate: If true, do not validate the manifest. - """ - - no_validate: Optional[bool] = None - - class WrapManifestVersions(Enum): """The versions of the Wrap manifest.""" @@ -68,3 +57,24 @@ class WrapAbiVersions(Enum): LATEST_WRAP_MANIFEST_VERSION = "0.1" LATEST_WRAP_ABI_VERSION = "0.1" + +__all__ = [ + # Options + "DeserializeManifestOptions", + # Enums + "WrapManifestVersions", + "WrapManifestAbiVersions", + "WrapAbiVersions", + # Concrete Versions + "WrapManifest_0_1", + "WrapAbi_0_1_0_1", + # Any Versions + "AnyWrapManifest", + "AnyWrapAbi", + # Latest Versions + "WrapManifest", + "WrapAbi", + # Latest Version constants + "LATEST_WRAP_MANIFEST_VERSION", + "LATEST_WRAP_ABI_VERSION", +] diff --git a/packages/polywrap-msgpack/polywrap_msgpack/__init__.py b/packages/polywrap-msgpack/polywrap_msgpack/__init__.py index fbf10d10..096032d9 100644 --- a/packages/polywrap-msgpack/polywrap_msgpack/__init__.py +++ b/packages/polywrap-msgpack/polywrap_msgpack/__init__.py @@ -14,13 +14,18 @@ from .sanitize import * __all__ = [ + # Serializer "msgpack_decode", "msgpack_encode", + # Extensions "decode_ext_hook", "encode_ext_hook", - "sanitize", "ExtensionTypes", + # Sanitizers + "sanitize", + # Extention types "GenericMap", + # Errors "MsgpackError", "MsgpackDecodeError", "MsgpackEncodeError", diff --git a/packages/polywrap-msgpack/polywrap_msgpack/decoder.py b/packages/polywrap-msgpack/polywrap_msgpack/decoder.py index 9d78a9d7..a768be9b 100644 --- a/packages/polywrap-msgpack/polywrap_msgpack/decoder.py +++ b/packages/polywrap-msgpack/polywrap_msgpack/decoder.py @@ -43,8 +43,8 @@ def msgpack_decode(val: bytes) -> Any: Any: any python object """ try: - return msgpack.unpackb( + return msgpack.unpackb( # pyright: ignore[reportUnknownMemberType] val, ext_hook=decode_ext_hook - ) # pyright: reportUnknownMemberType=false + ) except Exception as e: raise MsgpackDecodeError("Failed to decode msgpack data") from e diff --git a/packages/polywrap-msgpack/polywrap_msgpack/encoder.py b/packages/polywrap-msgpack/polywrap_msgpack/encoder.py index a74642b1..71ea9c16 100644 --- a/packages/polywrap-msgpack/polywrap_msgpack/encoder.py +++ b/packages/polywrap-msgpack/polywrap_msgpack/encoder.py @@ -28,8 +28,12 @@ def encode_ext_hook(obj: Any) -> ExtType: return ExtType( ExtensionTypes.GENERIC_MAP.value, # pylint: disable=protected-access - msgpack_encode(cast(GenericMap[Any, Any], obj)._map), - ) # pyright: reportPrivateUsage=false + msgpack_encode( + cast( + GenericMap[Any, Any], obj + )._map # pyright: ignore[reportPrivateUsage] + ), + ) raise MsgpackExtError(f"Object of type {type(obj)} is not supported") diff --git a/packages/polywrap-msgpack/polywrap_msgpack/sanitize.py b/packages/polywrap-msgpack/polywrap_msgpack/sanitize.py index e2982e36..93d8ba1b 100644 --- a/packages/polywrap-msgpack/polywrap_msgpack/sanitize.py +++ b/packages/polywrap-msgpack/polywrap_msgpack/sanitize.py @@ -22,7 +22,7 @@ def sanitize(value: Any) -> Any: if isinstance(value, GenericMap): dictionary: Dict[Any, Any] = cast( GenericMap[Any, Any], value - )._map # pyright: reportPrivateUsage=false + )._map # pyright: ignore[reportPrivateUsage] new_map: GenericMap[str, Any] = GenericMap({}) for key, val in dictionary.items(): if not isinstance(key, str): diff --git a/packages/polywrap-plugin/node_modules/.yarn-integrity b/packages/polywrap-plugin/node_modules/.yarn-integrity deleted file mode 100644 index 103d87c5..00000000 --- a/packages/polywrap-plugin/node_modules/.yarn-integrity +++ /dev/null @@ -1,30 +0,0 @@ -{ - "systemParams": "darwin-arm64-93", - "modulesFolders": [], - "flags": [], - "linkedModules": [ - "@coordinape/hardhat", - "@polywrap/client-js", - "@polywrap/core-js", - "@polywrap/msgpack-js", - "@polywrap/polywrap-manifest-types-js", - "@polywrap/schema-bind", - "@polywrap/schema-compose", - "@polywrap/schema-parse", - "@polywrap/wrap-manifest-types-js", - "@web3api/cli", - "@web3api/client-js", - "@web3api/core-js", - "@web3api/ethereum-plugin-js", - "@web3api/ipfs-plugin-js", - "@web3api/schema-bind", - "@web3api/schema-compose", - "@web3api/test-env-js", - "@web3api/wasm-as", - "polywrap" - ], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/packages/polywrap-plugin/poetry.lock b/packages/polywrap-plugin/poetry.lock index 8a7257a8..97b7dda8 100644 --- a/packages/polywrap-plugin/poetry.lock +++ b/packages/polywrap-plugin/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.4" +version = "2.15.5" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.4-py3-none-any.whl", hash = "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347"}, - {file = "astroid-2.15.4.tar.gz", hash = "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8"}, + {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, + {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, ] [package.dependencies] @@ -273,42 +273,41 @@ files = [ [[package]] name = "libcst" -version = "0.4.9" +version = "0.4.10" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7, 3.8, 3.9, and 3.10 programs." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "libcst-0.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f9e42085c403e22201e5c41e707ef73e4ea910ad9fc67983ceee2368097f54e"}, - {file = "libcst-0.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1266530bf840cc40633a04feb578bb4cac1aa3aea058cc3729e24eab09a8e996"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9679177391ccb9b0cdde3185c22bf366cb672457c4b7f4031fcb3b5e739fbd6"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d67bc87e0d8db9434f2ea063734938a320f541f4c6da1074001e372f840f385d"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e316da5a126f2a9e1d7680f95f907b575f082a35e2f8bd5620c59b2aaaebfe0a"}, - {file = "libcst-0.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:7415569ab998a85b0fc9af3a204611ea7fadb2d719a12532c448f8fc98f5aca4"}, - {file = "libcst-0.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:15ded11ff7f4572f91635e02b519ae959f782689fdb4445bbebb7a3cc5c71d75"}, - {file = "libcst-0.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b266867b712a120fad93983de432ddb2ccb062eb5fd2bea748c9a94cb200c36"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045b3b0b06413cdae6e9751b5f417f789ffa410f2cb2815e3e0e0ea6bef10ec0"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e799add8fba4976628b9c1a6768d73178bf898f0ed1bd1322930c2d3db9063ba"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10479371d04ee8dc978c889c1774bbf6a83df88fa055fcb0159a606f6679c565"}, - {file = "libcst-0.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:7a98286cbbfa90a42d376900c875161ad02a5a2a6b7c94c0f7afd9075e329ce4"}, - {file = "libcst-0.4.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:400166fc4efb9aa06ce44498d443aa78519082695b1894202dd73cd507d2d712"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46123863fba35cc84f7b54dd68826419cabfd9504d8a101c7fe3313ea03776f9"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27be8db54c0e5fe440021a771a38b81a7dbc23cd630eb8b0e9828b7717f9b702"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:132bec627b064bd567e7e4cd6c89524d02842151eb0d8f5f3f7ffd2579ec1b09"}, - {file = "libcst-0.4.9-cp37-cp37m-win_amd64.whl", hash = "sha256:596860090aeed3ee6ad1e59c35c6c4110a57e4e896abf51b91cae003ec720a11"}, - {file = "libcst-0.4.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4487608258109f774300466d4ca97353df29ae6ac23d1502e13e5509423c9d5"}, - {file = "libcst-0.4.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa53993e9a2853efb3ed3605da39f2e7125df6430f613eb67ef886c1ce4f94b5"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6ce794483d4c605ef0f5b199a49fb6996f9586ca938b7bfef213bd13858d7ab"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786e562b54bbcd17a060d1244deeef466b7ee07fe544074c252c4a169e38f1ee"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794250d2359edd518fb698e5d21c38a5bdfc5e4a75d0407b4c19818271ce6742"}, - {file = "libcst-0.4.9-cp38-cp38-win_amd64.whl", hash = "sha256:76491f67431318c3145442e97dddcead7075b074c59eac51be7cc9e3fffec6ee"}, - {file = "libcst-0.4.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3cf48d7aec6dc54b02aec0b1bb413c5bb3b02d852fd6facf1f05c7213e61a176"}, - {file = "libcst-0.4.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b3348c6b7711a5235b133bd8e11d22e903c388db42485b8ceb5f2aa0fae9b9f"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e33b66762efaa014c38819efae5d8f726dd823e32d5d691035484411d2a2a69"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1350d375d3fb9b20a6cf10c09b2964baca9be753a033dde7c1aced49d8e58387"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3822056dc13326082362db35b3f649e0f4a97e36ddb4e487441da8e0fb9db7b3"}, - {file = "libcst-0.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:183636141b839aa35b639e100883813744523bc7c12528906621121731b28443"}, - {file = "libcst-0.4.9.tar.gz", hash = "sha256:01786c403348f76f274dbaf3888ae237ffb73e6ed6973e65eba5c1fc389861dd"}, + {file = "libcst-0.4.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8fa0ec646ed7bce984d0ee9dbf514af278050bdb16a4fb986e916ace534eebc6"}, + {file = "libcst-0.4.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3cb3b7821eac00713844cda079583230c546a589b22ed5f03f2ddc4f985c384b"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7acfa747112ae40b032739661abd7c81aff37191294f7c2dab8bbd72372e78f"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1312e293b864ef3cb4b09534ed5f104c2dc45b680233c68bf76237295041c781"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76884b1afe475e8e68e704bf26eb9f9a2867029643e58f2f26a0286e3b6e998e"}, + {file = "libcst-0.4.10-cp310-cp310-win_amd64.whl", hash = "sha256:1069b808a711db5cd47538f27eb2c73206317aa0d8b5a3500b23aab24f86eb2e"}, + {file = "libcst-0.4.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:50be085346a35812535c7f876319689e15a7bfd1bd8efae8fd70589281d944b6"}, + {file = "libcst-0.4.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb9f10e5763e361e8bd8ff765fc0f1bcf744f242ff8b6d3e50ffec4dda3972ac"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfeeabb528b5df7b4be1817b584ce79e9a1a66687bd72f6de9c22272462812f1"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5648aeae8c90a2abab1f7b1bf205769a0179ed2cfe1ea7f681f6885e87b8b193"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a144f20aff4643b00374facf8409d30c7935db8176e5b2a07e1fd44004db2c1f"}, + {file = "libcst-0.4.10-cp311-cp311-win_amd64.whl", hash = "sha256:a10adc2e8ea2dda2b70eabec631ead2fc4a7a7ab633d6c2b690823c698b8431a"}, + {file = "libcst-0.4.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58fe90458a26a55358207f74abf8a05dff51d662069f070b4bd308a000a80c09"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:999fbbe467f61cbce9e6e054f86cd1c5ffa3740fd3dc8ebdd600db379f699256"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ee7e7be4efac4c140a97d772e1f6b3553f98fa5f46ad78df5dfe51e5a4aa4d"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:158478e8f45578fb26621b3dc0fe275f9e004297e9afdcf08936ecda05681174"}, + {file = "libcst-0.4.10-cp37-cp37m-win_amd64.whl", hash = "sha256:5ed101fee1af7abea3684fcff7fab5b170ceea4040756f54c15c870539daec66"}, + {file = "libcst-0.4.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:349f2b4ee4b982fe254c65c78d941fc96299f3c422b79f95ef8c7bba2b7f0f90"}, + {file = "libcst-0.4.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7cfa4d4beb84d0d63247aca27f1a15c63984512274c5b23040f8b4ba511036d7"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24582506da24e31f2644f862f11413a6b80fbad68d15194bfcc3f7dfebf2ec5e"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cdf2d0157438d3d52d310b0b6be31ff99bed19de489b2ebd3e2a4cd9946da45"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a677103d2f1ab0e50bc3a7cc6c96c7d64bcbac826d785e4cbf5ee9aaa9fcfa25"}, + {file = "libcst-0.4.10-cp38-cp38-win_amd64.whl", hash = "sha256:a8fdfd4a7d301adb785aa4b98e4a7cca45c5ff8cfb460b485d081efcfaaeeab7"}, + {file = "libcst-0.4.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b1569d87536bed4e9c11dd5c94a137dc0bce2a2b05961489c6016bf4521bb7cf"}, + {file = "libcst-0.4.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:72dff8783ac79cd10f2bd2fde0b28f262e9a22718ae26990948ba6131b85ca8b"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76adc53660ef094ff83f77a2550a7e00d1cab8e5e63336e071c17c09b5a89fe2"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3e9d9fdd9a9b9b8991936ff1c07527ce7ef396c8233280ba9a7137e72c2e48e"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e1b4cbaf7b1cdad5fa3eababe42d5b46c0d52afe13c5ba4eac2495fc57630ea"}, + {file = "libcst-0.4.10-cp39-cp39-win_amd64.whl", hash = "sha256:bcbd07cec3d7a7be6f0299b0c246e085e3d6cc8af367e2c96059183b97c2e2fe"}, ] [package.dependencies] @@ -317,7 +316,7 @@ typing-extensions = ">=3.7.4.2" typing-inspect = ">=0.4.0" [package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==22.10.0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8,<5)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.2)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<0.14)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.9)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.0.1)", "usort (==1.0.5)"] +dev = ["Sphinx (>=5.1.1)", "black (==23.1.0)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8,<5)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.2)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<0.14)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.10)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.1.0)", "usort (==1.0.6)"] [[package]] name = "markdown-it-py" @@ -455,14 +454,14 @@ files = [ [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] @@ -506,18 +505,18 @@ files = [ [[package]] name = "platformdirs" -version = "3.5.0" +version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.0-py3-none-any.whl", hash = "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4"}, - {file = "platformdirs-3.5.0.tar.gz", hash = "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"}, + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] @@ -737,14 +736,14 @@ testutils = ["gitpython (>3)"] [[package]] name = "pyright" -version = "1.1.306" +version = "1.1.309" description = "Command line wrapper for pyright" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.306-py3-none-any.whl", hash = "sha256:008eb2a29584ae274a154d749cf81476a3073fb562a462eac8d43a753378b9db"}, - {file = "pyright-1.1.306.tar.gz", hash = "sha256:16d5d198be64de497d5f9002000a271176c381e21b977ca5566cf779b643c9ed"}, + {file = "pyright-1.1.309-py3-none-any.whl", hash = "sha256:a8b052c1997f7334e80074998ea0f93bd149550e8cf27ccb5481d3b2e1aad161"}, + {file = "pyright-1.1.309.tar.gz", hash = "sha256:1abcfa83814d792a5d70b38621cc6489acfade94ebb2279e55ba1f394d54296c"}, ] [package.dependencies] @@ -866,19 +865,19 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -919,14 +918,14 @@ files = [ [[package]] name = "stevedore" -version = "5.0.0" +version = "5.1.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "stevedore-5.0.0-py3-none-any.whl", hash = "sha256:bd5a71ff5e5e5f5ea983880e4a1dd1bb47f8feebbb3d95b592398e2f02194771"}, - {file = "stevedore-5.0.0.tar.gz", hash = "sha256:2c428d2338976279e8eb2196f7a94910960d9f7ba2f41f3988511e95ca447021"}, + {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, + {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, ] [package.dependencies] @@ -1037,14 +1036,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.0-py3-none-any.whl", hash = "sha256:6ad00b63f849b7dcc313b70b6b304ed67b2b2963b3098a33efe18056b1a9a223"}, + {file = "typing_extensions-4.6.0.tar.gz", hash = "sha256:ff6b238610c747e44c268aa4bb23c8c735d665a63726df3f9431ce707f2aa768"}, ] [[package]] diff --git a/packages/polywrap-plugin/polywrap_plugin/module.py b/packages/polywrap-plugin/polywrap_plugin/module.py index 8fe6ec06..fea82e2e 100644 --- a/packages/polywrap-plugin/polywrap_plugin/module.py +++ b/packages/polywrap-plugin/polywrap_plugin/module.py @@ -1,21 +1,42 @@ """This module contains the PluginModule class.""" # pylint: disable=invalid-name from abc import ABC -from typing import Any, Generic, TypeVar +from dataclasses import dataclass +from typing import Any, Generic, Optional, TypeVar from polywrap_core import ( - InvokeOptions, - Invoker, - UriPackageOrWrapper, + InvokerClient, + Uri, + UriResolutionContext, WrapAbortError, WrapInvocationError, - execute_maybe_async_function, ) from polywrap_msgpack import msgpack_decode TConfig = TypeVar("TConfig") +@dataclass(kw_only=True, slots=True) +class InvokeOptions: + """InvokeOptions is a dataclass that holds the options for an invocation. + + Attributes: + uri: The URI of the wrapper. + method: The method to invoke. + args: The arguments to pass to the method. + env: The environment variables to set for the invocation. + resolution_context: A URI resolution context. + client: The client to use for subinvocations. + """ + + uri: Uri + method: str + args: Optional[Any] = None + env: Optional[Any] = None + resolution_context: Optional[UriResolutionContext] = None + client: Optional[InvokerClient] = None + + class PluginModule(Generic[TConfig], ABC): """PluginModule is the base class for all plugin modules. @@ -33,20 +54,17 @@ def __init__(self, config: TConfig): """ self.config = config - async def __wrap_invoke__( + def __wrap_invoke__( self, - options: InvokeOptions[UriPackageOrWrapper], - invoker: Invoker[UriPackageOrWrapper], + options: InvokeOptions, ) -> Any: """Invoke a method on the plugin. Args: - method: The name of the method to invoke. - args: The arguments to pass to the method. - invoker: The invoker to use for subinvocations. + options: The options to use when invoking the plugin. Returns: - The result of the plugin method invocation or an error. + The result of the plugin method invocation. """ if not hasattr(self, options.method): raise WrapInvocationError( @@ -61,11 +79,12 @@ async def __wrap_invoke__( if isinstance(options.args, bytes) else options.args ) - return await execute_maybe_async_function( - callable_method, decoded_args, invoker, options.env - ) + return callable_method(decoded_args, options.client, options.env) except Exception as err: raise WrapAbortError(options, repr(err)) from err raise WrapInvocationError( options, f"{options.method} is not a callable method in plugin module" ) + + +__all__ = ["PluginModule"] diff --git a/packages/polywrap-plugin/polywrap_plugin/package.py b/packages/polywrap-plugin/polywrap_plugin/package.py index 609da1ad..4592a348 100644 --- a/packages/polywrap-plugin/polywrap_plugin/package.py +++ b/packages/polywrap-plugin/polywrap_plugin/package.py @@ -2,8 +2,8 @@ # pylint: disable=invalid-name from typing import Generic, Optional, TypeVar -from polywrap_core import GetManifestOptions, UriPackageOrWrapper, WrapPackage, Wrapper -from polywrap_manifest import AnyWrapManifest +from polywrap_core import WrapPackage, Wrapper +from polywrap_manifest import AnyWrapManifest, DeserializeManifestOptions from .module import PluginModule from .wrapper import PluginWrapper @@ -11,7 +11,7 @@ TConfig = TypeVar("TConfig") -class PluginPackage(Generic[TConfig], WrapPackage[UriPackageOrWrapper]): +class PluginPackage(WrapPackage, Generic[TConfig]): """PluginPackage implements IWrapPackage interface for the plugin. Attributes: @@ -32,12 +32,12 @@ def __init__(self, module: PluginModule[TConfig], manifest: AnyWrapManifest): self.module = module self.manifest = manifest - async def create_wrapper(self) -> Wrapper[UriPackageOrWrapper]: + def create_wrapper(self) -> Wrapper: """Create a new plugin wrapper instance.""" return PluginWrapper(module=self.module, manifest=self.manifest) - async def get_manifest( - self, options: Optional[GetManifestOptions] = None + def get_manifest( + self, options: Optional[DeserializeManifestOptions] = None ) -> AnyWrapManifest: """Get the manifest of the plugin. @@ -48,3 +48,6 @@ async def get_manifest( The manifest of the plugin. """ return self.manifest + + +__all__ = ["PluginPackage"] diff --git a/packages/polywrap-plugin/polywrap_plugin/resolution_context_override_client.py b/packages/polywrap-plugin/polywrap_plugin/resolution_context_override_client.py new file mode 100644 index 00000000..3700f72f --- /dev/null +++ b/packages/polywrap-plugin/polywrap_plugin/resolution_context_override_client.py @@ -0,0 +1,84 @@ +"""This module defines the ResolutionContextOverrideClient class.""" +from typing import Any, List, Optional + +from polywrap_core import InvokerClient, Uri, UriResolutionContext + + +class ResolutionContextOverrideClient(InvokerClient): + """A client that overrides the resolution context of the wrapped client. + + Args: + client (InvokerClient): The wrapped client. + resolution_context (Optional[UriResolutionContext]): The resolution context to use. + """ + + client: InvokerClient + resolution_context: Optional[UriResolutionContext] + + __slots__ = ("client", "resolution_context") + + def __init__( + self, client: InvokerClient, resolution_context: Optional[UriResolutionContext] + ): + """Initialize a new ResolutionContextOverrideClient instance.""" + self.client = client + self.resolution_context = resolution_context + + def invoke( + self, + uri: Uri, + method: str, + args: Optional[Any] = None, + env: Optional[Any] = None, + resolution_context: Optional[UriResolutionContext] = None, + encode_result: Optional[bool] = False, + ) -> Any: + """Invoke the Wrapper based on the provided InvokerOptions. + + Args: + uri (Uri): Uri of the wrapper + method (str): Method to be executed + args (Optional[Any]) : Arguments for the method, structured as a dictionary + env (Optional[Any]): Override the client's config for all invokes within this invoke. + resolution_context (Optional[UriResolutionContext]): A URI resolution context + encode_result (Optional[bool]): If True, the result will be encoded + + Returns: + Any: invocation result. + """ + return self.client.invoke( + uri, + method, + args, + env, + self.resolution_context, + encode_result, + ) + + def get_implementations( + self, uri: Uri, apply_resolution: bool = True + ) -> Optional[List[Uri]]: + """Get implementations of an interface with its URI. + + Args: + uri (Uri): URI of the interface. + apply_resolution (bool): If True, apply resolution to the URI and interfaces. + + Returns: + Optional[List[Uri]]: List of implementations or None if not found. + """ + return self.client.get_implementations(uri, apply_resolution) + + def try_resolve_uri( + self, uri: Uri, resolution_context: UriResolutionContext | None = None + ) -> Any: + """Try to resolve a URI to a wrap package, a wrapper, or a URI. + + Args: + uri (Uri): The URI to resolve. + resolution_context (UriResolutionContext): The resolution context. + + Returns: + Any: URI Resolution Result. + """ + return self.client.try_resolve_uri(uri, self.resolution_context) diff --git a/packages/polywrap-plugin/polywrap_plugin/wrapper.py b/packages/polywrap-plugin/polywrap_plugin/wrapper.py index 49604e45..ac947758 100644 --- a/packages/polywrap-plugin/polywrap_plugin/wrapper.py +++ b/packages/polywrap-plugin/polywrap_plugin/wrapper.py @@ -1,24 +1,25 @@ """This module contains the PluginWrapper class.""" # pylint: disable=invalid-name -from typing import Generic, TypeVar, Union +# pylint: disable=too-many-arguments +from typing import Any, Generic, Optional, TypeVar, Union from polywrap_core import ( - GetFileOptions, InvocableResult, - InvokeOptions, - Invoker, - UriPackageOrWrapper, + InvokerClient, + Uri, + UriResolutionContext, Wrapper, ) from polywrap_manifest import AnyWrapManifest -from .module import PluginModule +from .module import InvokeOptions, PluginModule +from .resolution_context_override_client import ResolutionContextOverrideClient TConfig = TypeVar("TConfig") TResult = TypeVar("TResult") -class PluginWrapper(Generic[TConfig], Wrapper[UriPackageOrWrapper]): +class PluginWrapper(Wrapper, Generic[TConfig]): """PluginWrapper implements the Wrapper interface for plugin wrappers. Attributes: @@ -41,31 +42,53 @@ def __init__( self.module = module self.manifest = manifest - async def invoke( + def invoke( self, - options: InvokeOptions[UriPackageOrWrapper], - invoker: Invoker[UriPackageOrWrapper], + uri: Uri, + method: str, + args: Optional[Any] = None, + env: Optional[Any] = None, + resolution_context: Optional[UriResolutionContext] = None, + client: Optional[InvokerClient] = None, ) -> InvocableResult: - """Invoke a method on the plugin. + """Invoke the Wrapper based on the provided InvokeOptions. Args: - options (InvokeOptions): options to use when invoking the plugin. - invoker (Invoker): the invoker to use when invoking the plugin. + uri (Uri): Uri of the wrapper + method (str): Method to be executed + args (Optional[Any]) : Arguments for the method, structured as a dictionary + env (Optional[Any]): Override the client's config for all invokes within this invoke. + resolution_context (Optional[UriResolutionContext]): A URI resolution context + client (Optional[Invoker]): The invoker instance requesting this invocation.\ + This invoker will be used for any subinvocation that may occur. Returns: - Result[InvocableResult]: the result of the invocation. + InvocableResult: Result of the invocation. """ - result = await self.module.__wrap_invoke__(options, invoker) + options = InvokeOptions( + uri=uri, + method=method, + args=args, + env=env, + resolution_context=resolution_context, + client=ResolutionContextOverrideClient(client, resolution_context) + if client + else None, + ) + result = self.module.__wrap_invoke__(options) return InvocableResult(result=result, encoded=False) - async def get_file(self, options: GetFileOptions) -> Union[str, bytes]: - """Get a file from the plugin. + def get_file( + self, path: str, encoding: Optional[str] = "utf-8" + ) -> Union[str, bytes]: + """Get a file from the wrapper. Args: - options (GetFileOptions): options to use when getting the file. + path (str): Path to the file. + encoding (Optional[str]): Encoding of the file. Returns: - Result[Union[str, bytes]]: the file contents or an error. + Union[str, bytes]: The file contents """ raise NotImplementedError("client.get_file(..) is not implemented for plugins") @@ -76,3 +99,6 @@ def get_manifest(self) -> AnyWrapManifest: Result[AnyWrapManifest]: the manifest of the plugin. """ return self.manifest + + +__all__ = ["PluginWrapper"] diff --git a/packages/polywrap-plugin/pyproject.toml b/packages/polywrap-plugin/pyproject.toml index fa462b30..2f579e76 100644 --- a/packages/polywrap-plugin/pyproject.toml +++ b/packages/polywrap-plugin/pyproject.toml @@ -50,6 +50,7 @@ disable = [ "too-many-return-statements", "broad-exception-caught", "too-few-public-methods", + "too-many-arguments", ] ignore = [ "tests/" diff --git a/packages/polywrap-plugin/tests/conftest.py b/packages/polywrap-plugin/tests/conftest.py index 6fc7aa3b..d15900c0 100644 --- a/packages/polywrap-plugin/tests/conftest.py +++ b/packages/polywrap-plugin/tests/conftest.py @@ -2,15 +2,15 @@ from typing import Any, Dict, List, Union, Optional from polywrap_plugin import PluginModule -from polywrap_core import Invoker, Uri, InvokerOptions, UriPackageOrWrapper, Env +from polywrap_core import InvokerClient, Uri @fixture -def invoker() -> Invoker[UriPackageOrWrapper]: - class MockInvoker(Invoker[UriPackageOrWrapper]): - async def invoke(self, options: InvokerOptions[UriPackageOrWrapper]) -> Any: +def client() -> InvokerClient: + class MockInvoker(InvokerClient): + async def invoke(self, *args: Any) -> Any: raise NotImplementedError() - def get_implementations(self, uri: Uri) -> Union[List[Uri], None]: + def get_implementations(self, *args: Any) -> Union[List[Uri], None]: raise NotImplementedError() return MockInvoker() @@ -22,7 +22,7 @@ class GreetingModule(PluginModule[None]): def __init__(self, config: None): super().__init__(config) - def greeting(self, args: Dict[str, Any], client: Invoker[UriPackageOrWrapper], env: Optional[Env] = None): + def greeting(self, args: Dict[str, Any], client: InvokerClient, env: Optional[Any] = None): return f"Greetings from: {args['name']}" return GreetingModule(None) \ No newline at end of file diff --git a/packages/polywrap-plugin/tests/test_plugin_module.py b/packages/polywrap-plugin/tests/test_plugin_module.py index ef18454b..a308a276 100644 --- a/packages/polywrap-plugin/tests/test_plugin_module.py +++ b/packages/polywrap-plugin/tests/test_plugin_module.py @@ -1,16 +1,14 @@ -import pytest -from polywrap_core import Invoker, Uri, UriPackageOrWrapper, InvokeOptions +from polywrap_core import InvokerClient, Uri from polywrap_plugin import PluginModule +from polywrap_plugin.module import InvokeOptions -@pytest.mark.asyncio -async def test_plugin_module( - greeting_module: PluginModule[None], invoker: Invoker[UriPackageOrWrapper] +def test_plugin_module( + greeting_module: PluginModule[None], client: InvokerClient ): - result = await greeting_module.__wrap_invoke__( + result = greeting_module.__wrap_invoke__( InvokeOptions( - uri=Uri.from_str("plugin/greeting"), method="greeting", args={"name": "Joe"} + uri=Uri.from_str("plugin/greeting"), method="greeting", args={"name": "Joe"}, client=client ), - invoker, ) assert result, "Greetings from: Joe" diff --git a/packages/polywrap-plugin/tests/test_plugin_package.py b/packages/polywrap-plugin/tests/test_plugin_package.py index aa4dc9f4..5af64b23 100644 --- a/packages/polywrap-plugin/tests/test_plugin_package.py +++ b/packages/polywrap-plugin/tests/test_plugin_package.py @@ -1,24 +1,31 @@ from typing import cast -import pytest -from polywrap_core import InvokeOptions, Uri, Invoker, UriPackageOrWrapper +from polywrap_core import Uri, InvokerClient from polywrap_manifest import AnyWrapManifest from polywrap_plugin import PluginPackage, PluginModule -@pytest.mark.asyncio -async def test_plugin_package_invoke(greeting_module: PluginModule[None], invoker: Invoker[UriPackageOrWrapper]): +def test_plugin_package_invoke( + greeting_module: PluginModule[None], client: InvokerClient +): manifest = cast(AnyWrapManifest, {}) plugin_package = PluginPackage(greeting_module, manifest) - wrapper = await plugin_package.create_wrapper() - args = { - "name": "Joe" - } - options: InvokeOptions[UriPackageOrWrapper] = InvokeOptions( + wrapper = plugin_package.create_wrapper() + args = {"name": "Joe"} + + result = wrapper.invoke( uri=Uri.from_str("ens/greeting.eth"), method="greeting", - args=args + args=args, + client=client, ) + assert result, "Greetings from: Joe" + - result = await wrapper.invoke(options, invoker) - assert result, "Greetings from: Joe" \ No newline at end of file +def test_plugin_package_get_manifest( + greeting_module: PluginModule[None], client: InvokerClient +): + manifest = cast(AnyWrapManifest, {}) + plugin_package = PluginPackage(greeting_module, manifest) + + assert plugin_package.get_manifest() is manifest diff --git a/packages/polywrap-plugin/tests/test_plugin_wrapper.py b/packages/polywrap-plugin/tests/test_plugin_wrapper.py index 779b7328..25dcd8cf 100644 --- a/packages/polywrap-plugin/tests/test_plugin_wrapper.py +++ b/packages/polywrap-plugin/tests/test_plugin_wrapper.py @@ -1,24 +1,48 @@ from typing import cast -import pytest -from polywrap_core import InvokeOptions, Uri, Invoker, UriPackageOrWrapper +from polywrap_core import Uri, InvokerClient from polywrap_manifest import AnyWrapManifest from polywrap_plugin import PluginWrapper, PluginModule +import pytest -@pytest.mark.asyncio -async def test_plugin_wrapper_invoke(greeting_module: PluginModule[None], invoker: Invoker[UriPackageOrWrapper]): + +def test_plugin_wrapper_invoke( + greeting_module: PluginModule[None], client: InvokerClient +): manifest = cast(AnyWrapManifest, {}) wrapper = PluginWrapper(greeting_module, manifest) - args = { - "name": "Joe" - } - options: InvokeOptions[UriPackageOrWrapper] = InvokeOptions( + args = {"name": "Joe"} + + result = wrapper.invoke( uri=Uri.from_str("ens/greeting.eth"), method="greeting", - args=args + args=args, + client=client, ) + assert result, "Greetings from: Joe" + + +def test_plugin_wrapper_get_file( + greeting_module: PluginModule[None], client: InvokerClient +): + manifest = cast(AnyWrapManifest, {}) + + wrapper = PluginWrapper(greeting_module, manifest) + + with pytest.raises(NotImplementedError): + wrapper.get_file( + path="greeting.txt", + encoding="utf-8", + ) + + +def test_plugin_wrapper_manifest( + greeting_module: PluginModule[None], client: InvokerClient +): + manifest = cast(AnyWrapManifest, {}) + + wrapper = PluginWrapper(greeting_module, manifest) - result = await wrapper.invoke(options, invoker) - assert result, "Greetings from: Joe" \ No newline at end of file + assert wrapper.manifest is manifest diff --git a/packages/polywrap-wasm/polywrap_wasm/__init__.py b/packages/polywrap-wasm/polywrap_wasm/__init__.py index efc5276d..978aa2e1 100644 --- a/packages/polywrap-wasm/polywrap_wasm/__init__.py +++ b/packages/polywrap-wasm/polywrap_wasm/__init__.py @@ -1,8 +1,5 @@ """This module contains the runtime for executing Wasm wrappers.""" -from .buffer import * from .errors import * -from .exports import * -from .imports import * from .inmemory_file_reader import * from .wasm_package import * from .wasm_wrapper import * diff --git a/packages/polywrap-wasm/polywrap_wasm/constants.py b/packages/polywrap-wasm/polywrap_wasm/constants.py index 7645df03..3fcc99cd 100644 --- a/packages/polywrap-wasm/polywrap_wasm/constants.py +++ b/packages/polywrap-wasm/polywrap_wasm/constants.py @@ -1,3 +1,5 @@ """This module contains the constants used by polywrap-wasm package.""" WRAP_MANIFEST_PATH = "wrap.info" WRAP_MODULE_PATH = "wrap.wasm" + +__all__ = ["WRAP_MANIFEST_PATH", "WRAP_MODULE_PATH"] diff --git a/packages/polywrap-wasm/polywrap_wasm/errors.py b/packages/polywrap-wasm/polywrap_wasm/errors.py index 31b9bae9..07a6b58f 100644 --- a/packages/polywrap-wasm/polywrap_wasm/errors.py +++ b/packages/polywrap-wasm/polywrap_wasm/errors.py @@ -12,3 +12,10 @@ class WasmExportNotFoundError(WasmError): class WasmMemoryError(WasmError): """Raises when the Wasm memory is not found.""" + + +__all__ = [ + "WasmError", + "WasmExportNotFoundError", + "WasmMemoryError", +] diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/get_implementations.py b/packages/polywrap-wasm/polywrap_wasm/imports/get_implementations.py index 3ecd35f6..994ff671 100644 --- a/packages/polywrap-wasm/polywrap_wasm/imports/get_implementations.py +++ b/packages/polywrap-wasm/polywrap_wasm/imports/get_implementations.py @@ -24,6 +24,11 @@ def wrap_get_implementations(self, uri_ptr: int, uri_len: int) -> bool: uri_len, ) ) + if not self.invoker: + raise WrapAbortError( + invoke_options=self.state.invoke_options, + message="Expected invoker to be defined got None", + ) try: maybe_implementations = self.invoker.get_implementations(uri=uri) implementations: List[str] = ( @@ -35,8 +40,8 @@ def wrap_get_implementations(self, uri_ptr: int, uri_len: int) -> bool: return len(implementations) > 0 except Exception as err: raise WrapAbortError( - self.state.invoke_options, - f"failed calling invoker.get_implementations({repr(uri)})", + invoke_options=self.state.invoke_options, + message=f"failed calling invoker.get_implementations({repr(uri)})", ) from err def wrap_get_implementations_result_len(self) -> int: @@ -59,6 +64,12 @@ def wrap_get_implementations_result(self, ptr: int) -> None: self.write_bytes(ptr, result) def _get_get_implementations_result(self, export_name: str): + if not self.invoker: + raise WrapAbortError( + invoke_options=self.state.invoke_options, + message="Expected invoker to be defined got None", + ) + if not self.state.get_implementations_result: raise WrapAbortError( self.state.invoke_options, diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py b/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py index 78ecd91f..a1cdd5aa 100644 --- a/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py +++ b/packages/polywrap-wasm/polywrap_wasm/imports/subinvoke.py @@ -1,15 +1,10 @@ """This module contains the subinvoke imports for the Wasm module.""" -import asyncio -from concurrent.futures import ThreadPoolExecutor - -from polywrap_core import InvokerOptions, Uri, WrapAbortError +from polywrap_core import Uri, WrapAbortError from polywrap_msgpack import msgpack_encode from ..types import InvokeResult from .types import BaseWrapImports -pool = ThreadPoolExecutor() - class WrapSubinvokeImports(BaseWrapImports): """Defines the subinvoke family of imports for the Wasm module.""" @@ -42,18 +37,19 @@ def wrap_subinvoke( method = self._get_subinvoke_method(method_ptr, method_len) args = self._get_subinvoke_args(args_ptr, args_len) + if not self.invoker: + raise WrapAbortError( + invoke_options=self.state.invoke_options, + message="Expected invoker to be defined got None", + ) + try: - result = pool.submit( - asyncio.run, - self.invoker.invoke( - InvokerOptions( - uri=uri, - method=method, - args=args, - encode_result=True, - ) - ), - ).result() + result = self.invoker.invoke( + uri=uri, + method=method, + args=args, + encode_result=True, + ) if isinstance(result, bytes): self.state.subinvoke_result = InvokeResult(result=result) return True diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/types/base_wrap_imports.py b/packages/polywrap-wasm/polywrap_wasm/imports/types/base_wrap_imports.py index 58d4192e..46e7d9fc 100644 --- a/packages/polywrap-wasm/polywrap_wasm/imports/types/base_wrap_imports.py +++ b/packages/polywrap-wasm/polywrap_wasm/imports/types/base_wrap_imports.py @@ -2,8 +2,9 @@ from __future__ import annotations from abc import ABC +from typing import Optional -from polywrap_core import Invoker, UriPackageOrWrapper +from polywrap_core import Invoker from wasmtime import Memory, Store from ...buffer import read_bytes, read_string, write_bytes, write_string @@ -16,7 +17,7 @@ class BaseWrapImports(ABC): memory: Memory store: Store state: State - invoker: Invoker[UriPackageOrWrapper] + invoker: Optional[Invoker] def read_string(self, ptr: int, length: int) -> str: """Read a UTF-8 encoded string from the memory buffer.""" diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/utils/__init__.py b/packages/polywrap-wasm/polywrap_wasm/imports/utils/__init__.py deleted file mode 100644 index bcc32367..00000000 --- a/packages/polywrap-wasm/polywrap_wasm/imports/utils/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""This module contains utility functions for the Wasm imports.""" -from .unsync_invoke import * diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/utils/unsync_invoke.py b/packages/polywrap-wasm/polywrap_wasm/imports/utils/unsync_invoke.py deleted file mode 100644 index 600a5c59..00000000 --- a/packages/polywrap-wasm/polywrap_wasm/imports/utils/unsync_invoke.py +++ /dev/null @@ -1,13 +0,0 @@ -"""This module contains the unsync_invoke function.""" -from typing import Any - -from polywrap_core import Invoker, InvokerOptions, UriPackageOrWrapper -from unsync import Unfuture, unsync - - -@unsync -async def unsync_invoke( - invoker: Invoker[UriPackageOrWrapper], options: InvokerOptions[UriPackageOrWrapper] -) -> Unfuture[Any]: - """Perform an unsync invoke call.""" - return await invoker.invoke(options) diff --git a/packages/polywrap-wasm/polywrap_wasm/imports/wrap_imports.py b/packages/polywrap-wasm/polywrap_wasm/imports/wrap_imports.py index afefacfa..7246dbf6 100644 --- a/packages/polywrap-wasm/polywrap_wasm/imports/wrap_imports.py +++ b/packages/polywrap-wasm/polywrap_wasm/imports/wrap_imports.py @@ -3,7 +3,9 @@ # pylint: disable=too-many-ancestors from __future__ import annotations -from polywrap_core import Invoker, UriPackageOrWrapper +from typing import Optional + +from polywrap_core import Invoker from wasmtime import Memory, Store from ..types.state import State @@ -39,7 +41,7 @@ def __init__( memory: Memory, store: Store, state: State, - invoker: Invoker[UriPackageOrWrapper], + invoker: Optional[Invoker], ) -> None: """Initialize the WrapImports instance. diff --git a/packages/polywrap-wasm/polywrap_wasm/inmemory_file_reader.py b/packages/polywrap-wasm/polywrap_wasm/inmemory_file_reader.py index ea3d466f..da5c90d5 100644 --- a/packages/polywrap-wasm/polywrap_wasm/inmemory_file_reader.py +++ b/packages/polywrap-wasm/polywrap_wasm/inmemory_file_reader.py @@ -31,7 +31,7 @@ def __init__( self._wasm_manifest = wasm_manifest self._base_file_reader = base_file_reader - async def read_file(self, file_path: str) -> bytes: + def read_file(self, file_path: str) -> bytes: """Read a file from memory. Args: @@ -44,4 +44,7 @@ async def read_file(self, file_path: str) -> bytes: return self._wasm_module if file_path == WRAP_MANIFEST_PATH and self._wasm_manifest: return self._wasm_manifest - return await self._base_file_reader.read_file(file_path=file_path) + return self._base_file_reader.read_file(file_path=file_path) + + +__all__ = ["InMemoryFileReader"] diff --git a/packages/polywrap-wasm/polywrap_wasm/instance.py b/packages/polywrap-wasm/polywrap_wasm/instance.py index 0028cbb1..c1f4de53 100644 --- a/packages/polywrap-wasm/polywrap_wasm/instance.py +++ b/packages/polywrap-wasm/polywrap_wasm/instance.py @@ -1,5 +1,7 @@ """This module contains the imports of the Wasm wrapper module.""" -from polywrap_core import Invoker, UriPackageOrWrapper +from typing import Optional + +from polywrap_core import Invoker from wasmtime import Instance, Linker, Module, Store from .imports import WrapImports @@ -12,7 +14,7 @@ def create_instance( store: Store, module: bytes, state: State, - invoker: Invoker[UriPackageOrWrapper], + invoker: Optional[Invoker], ) -> Instance: """Create a Wasm instance for a Wasm module. diff --git a/packages/polywrap-wasm/polywrap_wasm/types/state.py b/packages/polywrap-wasm/polywrap_wasm/types/state.py index d9e18e5d..a09ecad6 100644 --- a/packages/polywrap-wasm/polywrap_wasm/types/state.py +++ b/packages/polywrap-wasm/polywrap_wasm/types/state.py @@ -1,12 +1,31 @@ """This module contains the State type for holding the state of a Wasm wrapper.""" from dataclasses import dataclass -from typing import Generic, Optional, TypeVar +from typing import Any, Generic, Optional, TypeVar -from polywrap_core import InvokeOptions, UriPackageOrWrapper +from polywrap_core import Uri, UriResolutionContext E = TypeVar("E") +@dataclass(kw_only=True, slots=True) +class InvokeOptions: + """InvokeOptions is a dataclass that holds the options for an invocation. + + Attributes: + uri: The URI of the wrapper. + method: The method to invoke. + args: The arguments to pass to the method. + env: The environment variables to set for the invocation. + resolution_context: A URI resolution context. + """ + + uri: Uri + method: str + args: Optional[dict[str, Any]] = None + env: Optional[dict[str, Any]] = None + resolution_context: Optional[UriResolutionContext] = None + + @dataclass(kw_only=True, slots=True) class InvokeResult(Generic[E]): """InvokeResult is a dataclass that holds the result of an invocation. @@ -39,7 +58,7 @@ class State: get_implementations_result: The result of a get implementations call. """ - invoke_options: InvokeOptions[UriPackageOrWrapper] + invoke_options: InvokeOptions invoke_result: Optional[InvokeResult[str]] = None subinvoke_result: Optional[InvokeResult[Exception]] = None get_implementations_result: Optional[bytes] = None diff --git a/packages/polywrap-wasm/polywrap_wasm/wasm_package.py b/packages/polywrap-wasm/polywrap_wasm/wasm_package.py index 55f43d62..7ab92c74 100644 --- a/packages/polywrap-wasm/polywrap_wasm/wasm_package.py +++ b/packages/polywrap-wasm/polywrap_wasm/wasm_package.py @@ -1,21 +1,19 @@ """This module contains the WasmPackage type for loading a Wasm package.""" from typing import Optional, Union -from polywrap_core import ( - FileReader, - GetManifestOptions, - UriPackageOrWrapper, - WrapPackage, - Wrapper, +from polywrap_core import FileReader, WrapPackage, Wrapper +from polywrap_manifest import ( + AnyWrapManifest, + DeserializeManifestOptions, + deserialize_wrap_manifest, ) -from polywrap_manifest import AnyWrapManifest, deserialize_wrap_manifest from .constants import WRAP_MANIFEST_PATH, WRAP_MODULE_PATH from .inmemory_file_reader import InMemoryFileReader from .wasm_wrapper import WasmWrapper -class WasmPackage(WrapPackage[UriPackageOrWrapper]): +class WasmPackage(WrapPackage): """WasmPackage is a type that represents a Wasm WRAP package. Attributes: @@ -43,8 +41,8 @@ def __init__( else file_reader ) - async def get_manifest( - self, options: Optional[GetManifestOptions] = None + def get_manifest( + self, options: Optional[DeserializeManifestOptions] = None ) -> AnyWrapManifest: """Get the manifest of the wrapper. @@ -54,13 +52,13 @@ async def get_manifest( if isinstance(self.manifest, AnyWrapManifest): return self.manifest - encoded_manifest = self.manifest or await self.file_reader.read_file( + encoded_manifest = self.manifest or self.file_reader.read_file( WRAP_MANIFEST_PATH ) manifest = deserialize_wrap_manifest(encoded_manifest, options) return manifest - async def get_wasm_module(self) -> bytes: + def get_wasm_module(self) -> bytes: """Get the Wasm module of the wrapper if it exists or return an error. Returns: @@ -69,13 +67,16 @@ async def get_wasm_module(self) -> bytes: if isinstance(self.wasm_module, bytes): return self.wasm_module - wasm_module = await self.file_reader.read_file(WRAP_MODULE_PATH) + wasm_module = self.file_reader.read_file(WRAP_MODULE_PATH) self.wasm_module = wasm_module return self.wasm_module - async def create_wrapper(self) -> Wrapper[UriPackageOrWrapper]: + def create_wrapper(self) -> Wrapper: """Create a new WasmWrapper instance.""" - wasm_module = await self.get_wasm_module() - wasm_manifest = await self.get_manifest() + wasm_module = self.get_wasm_module() + wasm_manifest = self.get_manifest() return WasmWrapper(self.file_reader, wasm_module, wasm_manifest) + + +__all__ = ["WasmPackage"] diff --git a/packages/polywrap-wasm/polywrap_wasm/wasm_wrapper.py b/packages/polywrap-wasm/polywrap_wasm/wasm_wrapper.py index 00ad2a12..a15fab67 100644 --- a/packages/polywrap-wasm/polywrap_wasm/wasm_wrapper.py +++ b/packages/polywrap-wasm/polywrap_wasm/wasm_wrapper.py @@ -1,14 +1,14 @@ """This module contains the WasmWrapper class for invoking Wasm wrappers.""" +# pylint: disable=too-many-locals from textwrap import dedent -from typing import Union +from typing import Any, Dict, Optional, Union from polywrap_core import ( FileReader, - GetFileOptions, InvocableResult, - InvokeOptions, Invoker, - UriPackageOrWrapper, + Uri, + UriResolutionContext, WrapAbortError, WrapError, Wrapper, @@ -19,10 +19,10 @@ from .exports import WrapExports from .instance import create_instance -from .types.state import State +from .types.state import InvokeOptions, State -class WasmWrapper(Wrapper[UriPackageOrWrapper]): +class WasmWrapper(Wrapper): """WasmWrapper implements the Wrapper interface for Wasm wrappers. Attributes: @@ -51,7 +51,9 @@ def get_wasm_module(self) -> bytes: """Get the Wasm module of the wrapper.""" return self.wasm_module - async def get_file(self, options: GetFileOptions) -> Union[str, bytes]: + def get_file( + self, path: str, encoding: Optional[str] = "utf-8" + ) -> Union[str, bytes]: """Get a file from the wrapper. Args: @@ -60,59 +62,67 @@ async def get_file(self, options: GetFileOptions) -> Union[str, bytes]: Returns: The file contents as string or bytes according to encoding or an error. """ - data = await self.file_reader.read_file(options.path) - return data.decode(encoding=options.encoding) if options.encoding else data + data = self.file_reader.read_file(path) + return data.decode(encoding=encoding) if encoding else data def create_wasm_instance( - self, - store: Store, - state: State, - invoker: Invoker[UriPackageOrWrapper], - options: InvokeOptions[UriPackageOrWrapper], + self, store: Store, state: State, client: Optional[Invoker] ) -> Instance: """Create a new Wasm instance for the wrapper. Args: store: The Wasm store to use when creating the instance. state: The Wasm wrapper state to use when creating the instance. - invoker: The invoker to use when creating the instance. + client: The client to use when creating the instance. Returns: The Wasm instance of the wrapper Wasm module. """ try: - return create_instance(store, self.wasm_module, state, invoker) + return create_instance(store, self.wasm_module, state, client) except Exception as err: raise WrapAbortError( - options, "Unable to instantiate the wasm module" + state.invoke_options, "Unable to instantiate the wasm module" ) from err - async def invoke( + def invoke( self, - options: InvokeOptions[UriPackageOrWrapper], - invoker: Invoker[UriPackageOrWrapper], + uri: Uri, + method: str, + args: Optional[Dict[str, Any]] = None, + env: Optional[Dict[str, Any]] = None, + resolution_context: Optional[UriResolutionContext] = None, + client: Optional[Invoker] = None, ) -> InvocableResult: """Invoke the wrapper. Args: options: The options to use when invoking the wrapper. - invoker: The invoker to use when invoking the wrapper. + client: The client to use when invoking the wrapper. Returns: The result of the invocation or an error. """ - if not (options.uri and options.method): + if not (uri and method): raise WrapError( dedent( f""" Expected invocation uri and method to be defiened got: - uri: {options.uri} - method: {options.method} + uri: {uri} + method: {method} """ ) ) - state = State(invoke_options=options) + state = State( + invoke_options=InvokeOptions( + uri=uri, + method=method, + args=args, + env=env, + resolution_context=resolution_context, + ) + ) encoded_args = ( state.invoke_options.args @@ -126,7 +136,7 @@ async def invoke( env_length = len(encoded_env) store = Store() - instance = self.create_wasm_instance(store, state, invoker, options) + instance = self.create_wasm_instance(store, state, client) exports = WrapExports(instance, store) result = exports.__wrap_invoke__(method_length, args_length, env_length) @@ -135,6 +145,9 @@ async def invoke( # Note: currently we only return not None result from Wasm module return InvocableResult(result=state.invoke_result.result, encoded=True) raise WrapAbortError( - options, + state.invoke_options, "Expected a result from the Wasm module", ) + + +__all__ = ["WasmWrapper"] diff --git a/packages/polywrap-wasm/tests/test_wasm_wrapper.py b/packages/polywrap-wasm/tests/test_wasm_wrapper.py index fe202feb..0ec2b998 100644 --- a/packages/polywrap-wasm/tests/test_wasm_wrapper.py +++ b/packages/polywrap-wasm/tests/test_wasm_wrapper.py @@ -1,22 +1,27 @@ -from typing import Any, List, cast +from typing import Any, cast import pytest from pathlib import Path from polywrap_msgpack import msgpack_decode -from polywrap_core import Uri, InvokeOptions, Invoker, InvokerOptions, UriPackageOrWrapper -from polywrap_wasm import FileReader, WasmPackage, WasmWrapper, WRAP_MODULE_PATH +from polywrap_core import InvokerClient, Uri, Invoker, FileReader +from polywrap_wasm import WasmPackage, WasmWrapper +from polywrap_wasm.constants import WRAP_MODULE_PATH, WRAP_MANIFEST_PATH from polywrap_manifest import deserialize_wrap_manifest -from polywrap_wasm.constants import WRAP_MANIFEST_PATH + @pytest.fixture def mock_invoker(): - class MockInvoker(Invoker[UriPackageOrWrapper]): - async def invoke(self, options: InvokerOptions[UriPackageOrWrapper]) -> Any: + class MockInvoker(InvokerClient): + + def try_resolve_uri(self, *args: Any, **kwargs: Any) -> Any: raise NotImplementedError() - - def get_implementations(self, uri: Uri) -> List[Uri]: + + def invoke(self, *args: Any, **kwargs: Any) -> Any: + raise NotImplementedError() + + def get_implementations(self, *args: Any, **kwargs: Any) -> Any: raise NotImplementedError() return MockInvoker() @@ -39,7 +44,7 @@ def simple_wrap_manifest(): @pytest.fixture def dummy_file_reader(): class DummyFileReader(FileReader): - async def read_file(self, file_path: str) -> bytes: + def read_file(self, *args: Any, **kwargs: Any) -> bytes: raise NotImplementedError() yield DummyFileReader() @@ -48,7 +53,7 @@ async def read_file(self, file_path: str) -> bytes: @pytest.fixture def simple_file_reader(simple_wrap_module: bytes, simple_wrap_manifest: bytes): class DummyFileReader(FileReader): - async def read_file(self, file_path: str) -> bytes: + def read_file(self, file_path: str) -> bytes: if file_path == WRAP_MODULE_PATH: return simple_wrap_module if file_path == WRAP_MANIFEST_PATH: @@ -58,28 +63,41 @@ async def read_file(self, file_path: str) -> bytes: yield DummyFileReader() -@pytest.mark.asyncio -async def test_invoke_with_wrapper( - dummy_file_reader: FileReader, simple_wrap_module: bytes, simple_wrap_manifest: bytes, mock_invoker: Invoker[UriPackageOrWrapper] +def test_invoke_with_wrapper( + dummy_file_reader: FileReader, + simple_wrap_module: bytes, + simple_wrap_manifest: bytes, + mock_invoker: Invoker, ): - wrapper = WasmWrapper(dummy_file_reader, simple_wrap_module, deserialize_wrap_manifest(simple_wrap_manifest)) + wrapper = WasmWrapper( + dummy_file_reader, + simple_wrap_module, + deserialize_wrap_manifest(simple_wrap_manifest), + ) message = "hey" args = {"arg": message} - options: InvokeOptions[UriPackageOrWrapper] = InvokeOptions(uri=Uri.from_str("fs/./build"), method="simpleMethod", args=args) - result = await wrapper.invoke(options, mock_invoker) + result = wrapper.invoke( + uri=Uri.from_str("fs/./build"), + method="simpleMethod", + args=args, + client=mock_invoker, + ) assert result.encoded is True assert msgpack_decode(cast(bytes, result.result)) == message -@pytest.mark.asyncio -async def test_invoke_with_package(simple_file_reader: FileReader, mock_invoker: Invoker[UriPackageOrWrapper]): +def test_invoke_with_package(simple_file_reader: FileReader, mock_invoker: InvokerClient): package = WasmPackage(simple_file_reader) - wrapper = await package.create_wrapper() + wrapper = package.create_wrapper() message = "hey" args = {"arg": message} - options: InvokeOptions[UriPackageOrWrapper] = InvokeOptions(uri=Uri.from_str("fs/./build"), method="simpleMethod", args=args) - result = await wrapper.invoke(options, mock_invoker) + result = wrapper.invoke( + uri=Uri.from_str("fs/./build"), + method="simpleMethod", + args=args, + client=mock_invoker, + ) assert result.encoded is True assert msgpack_decode(cast(bytes, result.result)) == message