Skip to content

Commit b56c6b2

Browse files
authored
Merge pull request #54 from polywrap/feat/ccb_refactor
Client Config Builder Module (Refactored)
2 parents 7d7a6ee + b20c6f1 commit b56c6b2

16 files changed

Lines changed: 2349 additions & 42 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
# Polywrap Python Client
3+
## This object allows you to build proper Polywrapt ClientConfig objects
4+
5+
These objects are needed to configure your python client's wrappers, pluggins, env variables, resolvers and interfaces.
6+
7+
Look at [this file](./polywrap_client_config_builder/client_config_builder.py) to detail all of its functionality
8+
And at [tests](./tests/test_client_config_builder.py)
9+
10+
---
11+
12+
The current implementation uses the `ClientConfig` as a dataclass to later create an Abstract Base Class of a `BaseClientConfigBuilder` which defines more clearly the functions of the module, like add_envs, set_resolvers, remove_wrappers, and so on.
13+
14+
This `BaseClientConfigBuilder` is later used in the class `ClientConfigBuilder` which only implements the build method, for now, and inherits all the abstract methods of the `BaseClientConfigBuilder`.

packages/polywrap-client-config-builder/poetry.lock

Lines changed: 1431 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""This package contains modules related to client config builder."""
2+
3+
from .client_config_builder import *
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"""This module provides a simple builder for building a ClientConfig object."""
2+
3+
from typing import Any, Dict, List, Optional, cast
4+
5+
from polywrap_core import (
6+
ClientConfig,
7+
Env,
8+
Uri,
9+
UriPackage,
10+
UriPackageOrWrapper,
11+
UriResolver,
12+
UriWrapper,
13+
WrapPackage,
14+
Wrapper,
15+
)
16+
from polywrap_uri_resolvers import (
17+
ExtendableUriResolver,
18+
InMemoryWrapperCache,
19+
PackageToWrapperResolver,
20+
RecursiveResolver,
21+
RequestSynchronizerResolver,
22+
StaticResolver,
23+
UriResolverAggregator,
24+
WrapperCacheResolver,
25+
)
26+
27+
from .types import BuilderConfig, BuildOptions
28+
29+
30+
class ClientConfigBuilder:
31+
"""Defines a simple builder for building a ClientConfig object.
32+
33+
The ClientConfigBuilder is used to create a ClientConfig object,\
34+
which is used to configure the Polywrap Client and its sub-components.\
35+
ClientConfigBuilder provides a simple interface for setting\
36+
the redirects, wrappers, packages, and other configuration options\
37+
for the Polywrap Client.
38+
"""
39+
40+
def __init__(self):
41+
"""Initialize the builder's config attributes with empty values."""
42+
self.config = BuilderConfig(
43+
envs={}, interfaces={}, resolvers=[], wrappers={}, packages={}, redirects={}
44+
)
45+
46+
def build(self, options: Optional[BuildOptions] = None) -> ClientConfig:
47+
"""Build the ClientConfig object from the builder's config."""
48+
resolver = (
49+
options.resolver
50+
if options and options.resolver
51+
else RecursiveResolver(
52+
RequestSynchronizerResolver(
53+
WrapperCacheResolver(
54+
PackageToWrapperResolver(
55+
UriResolverAggregator(
56+
[
57+
StaticResolver(self.config.redirects),
58+
StaticResolver(self.config.wrappers),
59+
StaticResolver(self.config.packages),
60+
*self.config.resolvers,
61+
ExtendableUriResolver(),
62+
]
63+
)
64+
),
65+
options.wrapper_cache
66+
if options and options.wrapper_cache
67+
else InMemoryWrapperCache(),
68+
)
69+
)
70+
)
71+
)
72+
73+
return ClientConfig(
74+
envs=self.config.envs,
75+
interfaces=self.config.interfaces,
76+
resolver=resolver,
77+
)
78+
79+
def add(self, config: BuilderConfig):
80+
"""Add the values from the given config to the builder's config."""
81+
if config.envs:
82+
self.config.envs.update(config.envs)
83+
if config.interfaces:
84+
self.config.interfaces.update(config.interfaces)
85+
if config.redirects:
86+
self.config.redirects.update(config.redirects)
87+
if config.resolvers:
88+
self.config.resolvers.extend(config.resolvers)
89+
if config.wrappers:
90+
self.config.wrappers.update(config.wrappers)
91+
if config.packages:
92+
self.config.packages.update(config.packages)
93+
return self
94+
95+
def get_envs(self) -> Dict[Uri, Dict[str, Any]]:
96+
"""Return the envs from the builder's config."""
97+
return self.config.envs
98+
99+
def set_env(self, uri: Uri, env: Env):
100+
"""Set the env by uri in the builder's config, overiding any existing values."""
101+
self.config.envs[uri] = env
102+
return self
103+
104+
def set_envs(self, uri_envs: Dict[Uri, Env]):
105+
"""Set the envs in the builder's config, overiding any existing values."""
106+
self.config.envs.update(uri_envs)
107+
return self
108+
109+
def add_env(self, uri: Uri, env: Env):
110+
"""Add an env for the given uri.
111+
112+
If an Env is already associated with the uri, it is modified.
113+
"""
114+
if self.config.envs.get(uri):
115+
for key in self.config.envs[uri]:
116+
self.config.envs[uri][key] = env[key]
117+
else:
118+
self.config.envs[uri] = env
119+
return self
120+
121+
def add_envs(self, uri_envs: Dict[Uri, Env]):
122+
"""Add a list of envs to the builder's config."""
123+
for uri, env in uri_envs.items():
124+
self.add_env(uri, env)
125+
return self
126+
127+
def add_interface_implementations(
128+
self, interface_uri: Uri, implementations_uris: List[Uri]
129+
):
130+
"""Add a list of implementation URIs for the given interface URI to the builder's config."""
131+
if interface_uri in self.config.interfaces.keys():
132+
self.config.interfaces[interface_uri].extend(implementations_uris)
133+
else:
134+
self.config.interfaces[interface_uri] = implementations_uris
135+
return self
136+
137+
def add_wrapper(self, uri: Uri, wrapper: Wrapper[UriPackageOrWrapper]):
138+
"""Add a wrapper by its URI to the builder's config."""
139+
self.config.wrappers[uri] = wrapper
140+
return self
141+
142+
def add_wrappers(self, uri_wrappers: List[UriWrapper[UriPackageOrWrapper]]):
143+
"""Add a list of URI-wrapper pairs to the builder's config."""
144+
for uri_wrapper in uri_wrappers:
145+
self.add_wrapper(cast(Uri, uri_wrapper), uri_wrapper.wrapper)
146+
return self
147+
148+
def remove_wrapper(self, uri: Uri):
149+
"""Remove a wrapper by its URI from the builder's config."""
150+
del self.config.wrappers[uri]
151+
return self
152+
153+
def remove_wrappers(self, uris: List[Uri]):
154+
"""Remove a list of wrappers by its URIs."""
155+
for uri in uris:
156+
self.remove_wrapper(uri)
157+
return self
158+
159+
def add_package(self, uri: Uri, package: WrapPackage[UriPackageOrWrapper]):
160+
"""Add a package by its URI to the builder's config."""
161+
self.config.packages[uri] = package
162+
return self
163+
164+
def add_packages(self, uri_packages: List[UriPackage[UriPackageOrWrapper]]):
165+
"""Add a list of URI-package pairs to the builder's config."""
166+
for uri_package in uri_packages:
167+
self.add_package(cast(Uri, uri_package), uri_package.package)
168+
return self
169+
170+
def remove_package(self, uri: Uri):
171+
"""Remove a package by its URI from the builder's config."""
172+
del self.config.packages[uri]
173+
return self
174+
175+
def remove_packages(self, uris: List[Uri]):
176+
"""Remove a list of packages by its URIs from the builder's config."""
177+
for uri in uris:
178+
self.remove_package(uri)
179+
return self
180+
181+
def add_resolver(self, resolver: UriResolver):
182+
"""Add a resolver to the builder's config."""
183+
self.config.resolvers.append(resolver)
184+
return self
185+
186+
def add_resolvers(self, resolvers_list: List[UriResolver]):
187+
"""Add a list of resolvers to the builder's config."""
188+
for resolver in resolvers_list:
189+
self.add_resolver(resolver)
190+
return self
191+
192+
def add_redirect(self, from_uri: Uri, to_uri: Uri):
193+
"""Add a URI redirect from `from_uri` to `to_uri`."""
194+
self.config.redirects[from_uri] = to_uri
195+
return self
196+
197+
def remove_redirect(self, from_uri: Uri):
198+
"""Remove a URI redirect by `from_uri`."""
199+
del self.config.redirects[from_uri]
200+
return self
201+
202+
def add_redirects(self, redirects: Dict[Uri, Uri]):
203+
"""Add a list of URI redirects to the builder's config."""
204+
self.config.redirects.update(redirects)
205+
return self
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""This package contains types related to client config builder."""
2+
from .build_options import *
3+
from .builder_config import *
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""This module contains the BuildOptions class."""
2+
from dataclasses import dataclass
3+
from typing import Optional
4+
5+
from polywrap_core import UriResolver
6+
from polywrap_uri_resolvers import WrapperCache
7+
8+
9+
@dataclass(slots=True, kw_only=True)
10+
class BuildOptions:
11+
"""
12+
Abstract class used to configure the polywrap client before it executes a call.
13+
14+
The ClientConfig class is created and modified with the ClientConfigBuilder module.
15+
"""
16+
17+
wrapper_cache: Optional[WrapperCache] = None
18+
resolver: Optional[UriResolver] = None
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""This module contains the BuilderConfig class."""
2+
from dataclasses import dataclass
3+
from typing import Any, Dict, List
4+
5+
from polywrap_core import Uri, UriPackageOrWrapper, UriResolver, WrapPackage, Wrapper
6+
7+
8+
@dataclass(slots=True, kw_only=True)
9+
class BuilderConfig:
10+
"""
11+
Abstract class used to configure the polywrap client before it executes a call.
12+
13+
The ClientConfig class is created and modified with the ClientConfigBuilder module.
14+
"""
15+
16+
envs: Dict[Uri, Dict[str, Any]]
17+
interfaces: Dict[Uri, List[Uri]]
18+
wrappers: Dict[Uri, Wrapper[UriPackageOrWrapper]]
19+
packages: Dict[Uri, WrapPackage[UriPackageOrWrapper]]
20+
resolvers: List[UriResolver]
21+
redirects: Dict[Uri, Uri]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[build-system]
2+
requires = ["poetry-core"]
3+
build-backend = "poetry.core.masonry.api"
4+
5+
[tool.poetry]
6+
name = "polywrap-client-config-builder"
7+
version = "0.1.0"
8+
description = ""
9+
authors = ["Media <media@polywrap.io>", "Cesar <cesar@polywrap.io>", "Niraj <niraj@polywrap.io>"]
10+
readme = "README.md"
11+
12+
[tool.poetry.dependencies]
13+
python = "^3.10"
14+
polywrap-core = { path = "../polywrap-core", develop = true }
15+
16+
[tool.poetry.dev-dependencies]
17+
polywrap-client = { path = "../polywrap-client", develop = true }
18+
pytest = "^7.1.2"
19+
pytest-asyncio = "^0.19.0"
20+
pylint = "^2.15.4"
21+
black = "^22.10.0"
22+
bandit = { version = "^1.7.4", extras = ["toml"]}
23+
tox = "^3.26.0"
24+
tox-poetry = "^0.4.1"
25+
isort = "^5.10.1"
26+
pyright = "^1.1.275"
27+
pydocstyle = "^6.1.1"
28+
29+
[tool.bandit]
30+
exclude_dirs = ["tests"]
31+
32+
[tool.black]
33+
target-version = ["py310"]
34+
35+
[tool.pyright]
36+
# default
37+
38+
[tool.pytest.ini_options]
39+
asyncio_mode = "auto"
40+
testpaths = [
41+
"tests"
42+
]
43+
44+
[tool.pylint]
45+
disable = [
46+
"too-many-return-statements",
47+
"too-many-public-methods",
48+
]
49+
ignore = [
50+
"tests/"
51+
]
52+
53+
[tool.isort]
54+
profile = "black"
55+
multi_line_output = 3
56+
57+
[tool.pydocstyle]
58+
# default
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from polywrap_core import Uri
2+
from pytest import fixture
3+
4+
# Variables
5+
6+
@fixture
7+
def env_varA():
8+
return { 'color': "yellow", 'size': "large" }
9+
10+
@fixture
11+
def env_varB():
12+
return { 'color': "red", 'size': "small" }
13+
14+
@fixture
15+
def env_varN():
16+
return { 'dog': "poodle", 'season': "summer" }
17+
18+
@fixture
19+
def env_varM():
20+
return { 'dog': "corgi", 'season': "autumn" }
21+
22+
@fixture
23+
def env_varS():
24+
return { 'vehicle': "scooter"}
25+
26+
# Uris
27+
28+
@fixture
29+
def env_uriX():
30+
return Uri.from_str("wrap://ens/eth.plugin.one/X")
31+
32+
@fixture
33+
def env_uriY():
34+
return Uri.from_str("wrap://ipfs/filecoin.wrapper.two/Y")
35+
36+
@fixture
37+
def env_uriZ():
38+
return Uri.from_str("wrap://pinlist/dev.wrappers.io/Z")

0 commit comments

Comments
 (0)