Skip to content

Commit 3161bf7

Browse files
authored
refactor: polywrap-plugin package (#184)
1 parent 838b616 commit 3161bf7

File tree

13 files changed

+295
-163
lines changed

13 files changed

+295
-163
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Encode - Decode need to be tested

packages/polywrap-client/tests/test_wasm_wrapper.py

Whitespace-only changes.

packages/polywrap-plugin/node_modules/.yarn-integrity

Lines changed: 0 additions & 30 deletions
This file was deleted.

packages/polywrap-plugin/poetry.lock

Lines changed: 55 additions & 56 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/polywrap-plugin/polywrap_plugin/module.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,42 @@
11
"""This module contains the PluginModule class."""
22
# pylint: disable=invalid-name
33
from abc import ABC
4-
from typing import Any, Generic, TypeVar
4+
from dataclasses import dataclass
5+
from typing import Any, Generic, Optional, TypeVar
56

67
from polywrap_core import (
7-
InvokeOptions,
8-
Invoker,
9-
UriPackageOrWrapper,
8+
InvokerClient,
9+
Uri,
10+
UriResolutionContext,
1011
WrapAbortError,
1112
WrapInvocationError,
12-
execute_maybe_async_function,
1313
)
1414
from polywrap_msgpack import msgpack_decode
1515

1616
TConfig = TypeVar("TConfig")
1717

1818

19+
@dataclass(kw_only=True, slots=True)
20+
class InvokeOptions:
21+
"""InvokeOptions is a dataclass that holds the options for an invocation.
22+
23+
Attributes:
24+
uri: The URI of the wrapper.
25+
method: The method to invoke.
26+
args: The arguments to pass to the method.
27+
env: The environment variables to set for the invocation.
28+
resolution_context: A URI resolution context.
29+
client: The client to use for subinvocations.
30+
"""
31+
32+
uri: Uri
33+
method: str
34+
args: Optional[Any] = None
35+
env: Optional[Any] = None
36+
resolution_context: Optional[UriResolutionContext] = None
37+
client: Optional[InvokerClient] = None
38+
39+
1940
class PluginModule(Generic[TConfig], ABC):
2041
"""PluginModule is the base class for all plugin modules.
2142
@@ -33,20 +54,17 @@ def __init__(self, config: TConfig):
3354
"""
3455
self.config = config
3556

36-
async def __wrap_invoke__(
57+
def __wrap_invoke__(
3758
self,
38-
options: InvokeOptions[UriPackageOrWrapper],
39-
invoker: Invoker[UriPackageOrWrapper],
59+
options: InvokeOptions,
4060
) -> Any:
4161
"""Invoke a method on the plugin.
4262
4363
Args:
44-
method: The name of the method to invoke.
45-
args: The arguments to pass to the method.
46-
invoker: The invoker to use for subinvocations.
64+
options: The options to use when invoking the plugin.
4765
4866
Returns:
49-
The result of the plugin method invocation or an error.
67+
The result of the plugin method invocation.
5068
"""
5169
if not hasattr(self, options.method):
5270
raise WrapInvocationError(
@@ -61,11 +79,12 @@ async def __wrap_invoke__(
6179
if isinstance(options.args, bytes)
6280
else options.args
6381
)
64-
return await execute_maybe_async_function(
65-
callable_method, decoded_args, invoker, options.env
66-
)
82+
return callable_method(decoded_args, options.client, options.env)
6783
except Exception as err:
6884
raise WrapAbortError(options, repr(err)) from err
6985
raise WrapInvocationError(
7086
options, f"{options.method} is not a callable method in plugin module"
7187
)
88+
89+
90+
__all__ = ["PluginModule"]

packages/polywrap-plugin/polywrap_plugin/package.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
# pylint: disable=invalid-name
33
from typing import Generic, Optional, TypeVar
44

5-
from polywrap_core import GetManifestOptions, UriPackageOrWrapper, WrapPackage, Wrapper
6-
from polywrap_manifest import AnyWrapManifest
5+
from polywrap_core import WrapPackage, Wrapper
6+
from polywrap_manifest import AnyWrapManifest, DeserializeManifestOptions
77

88
from .module import PluginModule
99
from .wrapper import PluginWrapper
1010

1111
TConfig = TypeVar("TConfig")
1212

1313

14-
class PluginPackage(Generic[TConfig], WrapPackage[UriPackageOrWrapper]):
14+
class PluginPackage(WrapPackage, Generic[TConfig]):
1515
"""PluginPackage implements IWrapPackage interface for the plugin.
1616
1717
Attributes:
@@ -32,12 +32,12 @@ def __init__(self, module: PluginModule[TConfig], manifest: AnyWrapManifest):
3232
self.module = module
3333
self.manifest = manifest
3434

35-
async def create_wrapper(self) -> Wrapper[UriPackageOrWrapper]:
35+
def create_wrapper(self) -> Wrapper:
3636
"""Create a new plugin wrapper instance."""
3737
return PluginWrapper(module=self.module, manifest=self.manifest)
3838

39-
async def get_manifest(
40-
self, options: Optional[GetManifestOptions] = None
39+
def get_manifest(
40+
self, options: Optional[DeserializeManifestOptions] = None
4141
) -> AnyWrapManifest:
4242
"""Get the manifest of the plugin.
4343
@@ -48,3 +48,6 @@ async def get_manifest(
4848
The manifest of the plugin.
4949
"""
5050
return self.manifest
51+
52+
53+
__all__ = ["PluginPackage"]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""This module defines the ResolutionContextOverrideClient class."""
2+
from typing import Any, List, Optional
3+
4+
from polywrap_core import InvokerClient, Uri, UriResolutionContext
5+
6+
7+
class ResolutionContextOverrideClient(InvokerClient):
8+
"""A client that overrides the resolution context of the wrapped client.
9+
10+
Args:
11+
client (InvokerClient): The wrapped client.
12+
resolution_context (Optional[UriResolutionContext]): The resolution context to use.
13+
"""
14+
15+
client: InvokerClient
16+
resolution_context: Optional[UriResolutionContext]
17+
18+
__slots__ = ("client", "resolution_context")
19+
20+
def __init__(
21+
self, client: InvokerClient, resolution_context: Optional[UriResolutionContext]
22+
):
23+
"""Initialize a new ResolutionContextOverrideClient instance."""
24+
self.client = client
25+
self.resolution_context = resolution_context
26+
27+
def invoke(
28+
self,
29+
uri: Uri,
30+
method: str,
31+
args: Optional[Any] = None,
32+
env: Optional[Any] = None,
33+
resolution_context: Optional[UriResolutionContext] = None,
34+
encode_result: Optional[bool] = False,
35+
) -> Any:
36+
"""Invoke the Wrapper based on the provided InvokerOptions.
37+
38+
Args:
39+
uri (Uri): Uri of the wrapper
40+
method (str): Method to be executed
41+
args (Optional[Any]) : Arguments for the method, structured as a dictionary
42+
env (Optional[Any]): Override the client's config for all invokes within this invoke.
43+
resolution_context (Optional[UriResolutionContext]): A URI resolution context
44+
encode_result (Optional[bool]): If True, the result will be encoded
45+
46+
Returns:
47+
Any: invocation result.
48+
"""
49+
return self.client.invoke(
50+
uri,
51+
method,
52+
args,
53+
env,
54+
self.resolution_context,
55+
encode_result,
56+
)
57+
58+
def get_implementations(
59+
self, uri: Uri, apply_resolution: bool = True
60+
) -> Optional[List[Uri]]:
61+
"""Get implementations of an interface with its URI.
62+
63+
Args:
64+
uri (Uri): URI of the interface.
65+
apply_resolution (bool): If True, apply resolution to the URI and interfaces.
66+
67+
Returns:
68+
Optional[List[Uri]]: List of implementations or None if not found.
69+
"""
70+
return self.client.get_implementations(uri, apply_resolution)
71+
72+
def try_resolve_uri(
73+
self, uri: Uri, resolution_context: UriResolutionContext | None = None
74+
) -> Any:
75+
"""Try to resolve a URI to a wrap package, a wrapper, or a URI.
76+
77+
Args:
78+
uri (Uri): The URI to resolve.
79+
resolution_context (UriResolutionContext): The resolution context.
80+
81+
Returns:
82+
Any: URI Resolution Result.
83+
"""
84+
return self.client.try_resolve_uri(uri, self.resolution_context)
Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
"""This module contains the PluginWrapper class."""
22
# pylint: disable=invalid-name
3-
from typing import Generic, TypeVar, Union
3+
# pylint: disable=too-many-arguments
4+
from typing import Any, Generic, Optional, TypeVar, Union
45

56
from polywrap_core import (
6-
GetFileOptions,
77
InvocableResult,
8-
InvokeOptions,
9-
Invoker,
10-
UriPackageOrWrapper,
8+
InvokerClient,
9+
Uri,
10+
UriResolutionContext,
1111
Wrapper,
1212
)
1313
from polywrap_manifest import AnyWrapManifest
1414

15-
from .module import PluginModule
15+
from .module import InvokeOptions, PluginModule
16+
from .resolution_context_override_client import ResolutionContextOverrideClient
1617

1718
TConfig = TypeVar("TConfig")
1819
TResult = TypeVar("TResult")
1920

2021

21-
class PluginWrapper(Generic[TConfig], Wrapper[UriPackageOrWrapper]):
22+
class PluginWrapper(Wrapper, Generic[TConfig]):
2223
"""PluginWrapper implements the Wrapper interface for plugin wrappers.
2324
2425
Attributes:
@@ -41,31 +42,53 @@ def __init__(
4142
self.module = module
4243
self.manifest = manifest
4344

44-
async def invoke(
45+
def invoke(
4546
self,
46-
options: InvokeOptions[UriPackageOrWrapper],
47-
invoker: Invoker[UriPackageOrWrapper],
47+
uri: Uri,
48+
method: str,
49+
args: Optional[Any] = None,
50+
env: Optional[Any] = None,
51+
resolution_context: Optional[UriResolutionContext] = None,
52+
client: Optional[InvokerClient] = None,
4853
) -> InvocableResult:
49-
"""Invoke a method on the plugin.
54+
"""Invoke the Wrapper based on the provided InvokeOptions.
5055
5156
Args:
52-
options (InvokeOptions): options to use when invoking the plugin.
53-
invoker (Invoker): the invoker to use when invoking the plugin.
57+
uri (Uri): Uri of the wrapper
58+
method (str): Method to be executed
59+
args (Optional[Any]) : Arguments for the method, structured as a dictionary
60+
env (Optional[Any]): Override the client's config for all invokes within this invoke.
61+
resolution_context (Optional[UriResolutionContext]): A URI resolution context
62+
client (Optional[Invoker]): The invoker instance requesting this invocation.\
63+
This invoker will be used for any subinvocation that may occur.
5464
5565
Returns:
56-
Result[InvocableResult]: the result of the invocation.
66+
InvocableResult: Result of the invocation.
5767
"""
58-
result = await self.module.__wrap_invoke__(options, invoker)
68+
options = InvokeOptions(
69+
uri=uri,
70+
method=method,
71+
args=args,
72+
env=env,
73+
resolution_context=resolution_context,
74+
client=ResolutionContextOverrideClient(client, resolution_context)
75+
if client
76+
else None,
77+
)
78+
result = self.module.__wrap_invoke__(options)
5979
return InvocableResult(result=result, encoded=False)
6080

61-
async def get_file(self, options: GetFileOptions) -> Union[str, bytes]:
62-
"""Get a file from the plugin.
81+
def get_file(
82+
self, path: str, encoding: Optional[str] = "utf-8"
83+
) -> Union[str, bytes]:
84+
"""Get a file from the wrapper.
6385
6486
Args:
65-
options (GetFileOptions): options to use when getting the file.
87+
path (str): Path to the file.
88+
encoding (Optional[str]): Encoding of the file.
6689
6790
Returns:
68-
Result[Union[str, bytes]]: the file contents or an error.
91+
Union[str, bytes]: The file contents
6992
"""
7093
raise NotImplementedError("client.get_file(..) is not implemented for plugins")
7194

@@ -76,3 +99,6 @@ def get_manifest(self) -> AnyWrapManifest:
7699
Result[AnyWrapManifest]: the manifest of the plugin.
77100
"""
78101
return self.manifest
102+
103+
104+
__all__ = ["PluginWrapper"]

packages/polywrap-plugin/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ disable = [
5050
"too-many-return-statements",
5151
"broad-exception-caught",
5252
"too-few-public-methods",
53+
"too-many-arguments",
5354
]
5455
ignore = [
5556
"tests/"

packages/polywrap-plugin/tests/conftest.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
from typing import Any, Dict, List, Union, Optional
33

44
from polywrap_plugin import PluginModule
5-
from polywrap_core import Invoker, Uri, InvokerOptions, UriPackageOrWrapper, Env
5+
from polywrap_core import InvokerClient, Uri
66

77
@fixture
8-
def invoker() -> Invoker[UriPackageOrWrapper]:
9-
class MockInvoker(Invoker[UriPackageOrWrapper]):
10-
async def invoke(self, options: InvokerOptions[UriPackageOrWrapper]) -> Any:
8+
def client() -> InvokerClient:
9+
class MockInvoker(InvokerClient):
10+
async def invoke(self, *args: Any) -> Any:
1111
raise NotImplementedError()
1212

13-
def get_implementations(self, uri: Uri) -> Union[List[Uri], None]:
13+
def get_implementations(self, *args: Any) -> Union[List[Uri], None]:
1414
raise NotImplementedError()
1515

1616
return MockInvoker()
@@ -22,7 +22,7 @@ class GreetingModule(PluginModule[None]):
2222
def __init__(self, config: None):
2323
super().__init__(config)
2424

25-
def greeting(self, args: Dict[str, Any], client: Invoker[UriPackageOrWrapper], env: Optional[Env] = None):
25+
def greeting(self, args: Dict[str, Any], client: InvokerClient, env: Optional[Any] = None):
2626
return f"Greetings from: {args['name']}"
2727

2828
return GreetingModule(None)

0 commit comments

Comments
 (0)