Skip to content

Commit 07b0031

Browse files
committed
wip: client config builder
1 parent 5e5b011 commit 07b0031

10 files changed

Lines changed: 994 additions & 953 deletions

File tree

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

Lines changed: 779 additions & 754 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
"""
2-
Polywrap Python Client.
3-
4-
ClientConfigBuilder Package
5-
6-
docs.polywrap.io
7-
8-
Copyright 2022 Polywrap
9-
"""
1+
"""This package contains modules related to client config builder."""
102

113
from .client_config_builder import *
Lines changed: 143 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,208 +1,204 @@
1-
"""
2-
Polywrap Python Client.
3-
4-
The ClientConfigBuilder Package provides a simple interface for building a ClientConfig object,
5-
which is used to configure the Polywrap Client and its sub-components. You can use the
6-
ClientConfigBuilder to set the wrappers, packages, and other configuration options for the
7-
Polywrap Client.
8-
9-
docs.polywrap.io
10-
Copyright 2022 Polywrap
11-
"""
12-
13-
from abc import ABC, abstractmethod
14-
from dataclasses import dataclass
15-
from typing import Any, Dict, List, Union
16-
17-
from polywrap_core import Env, Uri, UriPackage, UriWrapper
18-
19-
UriResolverLike = Union[Uri, UriPackage, UriWrapper, List["UriResolverLike"]]
20-
21-
22-
@dataclass(slots=True, kw_only=True)
23-
class ClientConfig:
24-
"""
25-
Abstract class used to configure the polywrap client before it executes a call.
26-
27-
The ClientConfig class is created and modified with the ClientConfigBuilder module.
28-
"""
29-
30-
envs: Dict[Uri, Dict[str, Any]]
31-
interfaces: Dict[Uri, List[Uri]]
32-
wrappers: List[UriWrapper]
33-
packages: List[UriPackage]
34-
resolver: List[UriResolverLike]
35-
redirects: Dict[Uri, Uri]
36-
37-
38-
class BaseClientConfigBuilder(ABC):
39-
"""
40-
An abstract base class of the `ClientConfigBuilder`.
41-
42-
It uses the ABC module to define the methods that can be used to
43-
configure the `ClientConfig` object.
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+
Env,
7+
Uri,
8+
UriPackage,
9+
UriWrapper,
10+
UriPackageOrWrapper,
11+
Wrapper,
12+
WrapPackage,
13+
UriResolver,
14+
)
15+
from polywrap_client import PolywrapClientConfig
16+
17+
from polywrap_uri_resolvers import (
18+
RecursiveResolver,
19+
RequestSynchronizerResolver,
20+
WrapperCacheResolver,
21+
PackageToWrapperResolver,
22+
StaticResolver,
23+
ExtendableUriResolver,
24+
InMemoryWrapperCache,
25+
UriResolverAggregator,
26+
)
27+
28+
from .types import BuilderConfig, BuildOptions
29+
30+
31+
class ClientConfigBuilder:
32+
"""Defines a simple builder for building a ClientConfig object.
33+
34+
The ClientConfigBuilder is used to create a ClientConfig object, which is used to configure
35+
the Polywrap Client and its sub-components. ClientConfigBuilder provides a simple interface
36+
for setting the redirects, wrappers, packages, and other configuration options for the Polywrap Client.
4437
"""
4538

4639
def __init__(self):
4740
"""Initialize the builder's config attributes with empty values."""
48-
self.config = ClientConfig(
49-
envs={}, interfaces={}, resolver=[], wrappers=[], packages=[], redirects={}
41+
self.config = BuilderConfig(
42+
envs={}, interfaces={}, resolvers=[], wrappers={}, packages={}, redirects={}
5043
)
5144

52-
@abstractmethod
53-
def build(self) -> ClientConfig:
54-
"""Return a sanitized config object from the builder's config."""
45+
def build(self, options: Optional[BuildOptions] = None) -> PolywrapClientConfig:
46+
"""Build the ClientConfig object from the builder's config."""
47+
resolver = (
48+
options.resolver
49+
if options and options.resolver
50+
else RecursiveResolver(
51+
RequestSynchronizerResolver(
52+
WrapperCacheResolver(
53+
PackageToWrapperResolver(
54+
UriResolverAggregator(
55+
[
56+
StaticResolver(self.config.redirects),
57+
StaticResolver(self.config.wrappers),
58+
StaticResolver(self.config.packages),
59+
*self.config.resolvers,
60+
ExtendableUriResolver(),
61+
]
62+
)
63+
),
64+
options.wrapper_cache
65+
if options and options.wrapper_cache
66+
else InMemoryWrapperCache(),
67+
)
68+
)
69+
)
70+
)
5571

56-
def add(self, new_config: ClientConfig):
57-
"""
58-
Return a sanitized config object from the builder's config.
72+
return PolywrapClientConfig(
73+
envs=self.config.envs,
74+
interfaces=self.config.interfaces,
75+
resolver=resolver,
76+
)
5977

60-
Input is a partial `ClientConfig` object.
61-
"""
62-
if new_config.envs:
63-
self.config.envs.update(new_config.envs)
64-
if new_config.interfaces:
65-
self.config.interfaces.update(new_config.interfaces)
66-
if new_config.resolver:
67-
self.config.resolver.extend(new_config.resolver)
68-
if new_config.wrappers:
69-
self.config.wrappers.extend(new_config.wrappers)
70-
if new_config.packages:
71-
self.config.packages.extend(new_config.packages)
78+
def add(self, config: BuilderConfig):
79+
"""Add the values from the given config to the builder's config."""
80+
if config.envs:
81+
self.config.envs.update(config.envs)
82+
if config.interfaces:
83+
self.config.interfaces.update(config.interfaces)
84+
if config.redirects:
85+
self.config.redirects.update(config.redirects)
86+
if config.resolvers:
87+
self.config.resolvers.extend(config.resolvers)
88+
if config.wrappers:
89+
self.config.wrappers.update(config.wrappers)
90+
if config.packages:
91+
self.config.packages.update(config.packages)
7292
return self
7393

7494
def get_envs(self) -> Dict[Uri, Dict[str, Any]]:
75-
"""Return the envs dictionary from the builder's config."""
95+
"""Return the envs from the builder's config."""
7696
return self.config.envs
7797

78-
def set_env(self, env: Env, uri: Uri):
79-
"""Set the envs dictionary in the builder's config, overiding any existing values."""
98+
def set_env(self, uri: Uri, env: Env):
99+
"""Set the env by uri in the builder's config, overiding any existing values."""
80100
self.config.envs[uri] = env
81101
return self
82102

83-
def add_env(self, env: Env, uri: Uri):
84-
"""
85-
Add an environment (in the form of an `Env`) for a given uri.
103+
def set_envs(self, uri_envs: Dict[Uri, Env]):
104+
"""Set the envs in the builder's config, overiding any existing values."""
105+
self.config.envs.update(uri_envs)
106+
return self
86107

87-
Note it is not overwriting existing environments, unless the
88-
env key already exists in the environment, then it will overwrite the existing value.
108+
def add_env(self, uri: Uri, env: Env):
109+
"""Add an env for the given uri.
110+
111+
If an Env is already associated with the uri, it is modified.
89112
"""
90-
if uri in self.config.envs.keys():
91-
for key in env.keys():
113+
if self.config.envs.get(uri):
114+
for key in self.config.envs[uri]:
92115
self.config.envs[uri][key] = env[key]
93116
else:
94117
self.config.envs[uri] = env
95118
return self
96119

97-
def add_envs(self, envs: List[Env], uri: Uri = None):
98-
"""Add a list of environments (each in the form of an `Env`) for a given uri."""
99-
for env in envs:
100-
self.add_env(env, uri)
120+
def add_envs(self, uri_envs: Dict[Uri, Env]):
121+
"""Add a list of envs to the builder's config."""
122+
for uri, env in uri_envs.items():
123+
self.add_env(uri, env)
101124
return self
102125

103126
def add_interface_implementations(
104127
self, interface_uri: Uri, implementations_uris: List[Uri]
105128
):
106-
"""Add a list of implementations (each in the form of an `Uri`) for a given interface."""
107-
if interface_uri is None:
108-
raise ValueError()
129+
"""Add a list of implementation URIs for the given interface URI to the builder's config."""
109130
if interface_uri in self.config.interfaces.keys():
110-
self.config.interfaces[interface_uri] = (
111-
self.config.interfaces[interface_uri] + implementations_uris
112-
)
131+
self.config.interfaces[interface_uri].extend(implementations_uris)
113132
else:
114133
self.config.interfaces[interface_uri] = implementations_uris
115134
return self
116135

117-
def add_wrapper(self, wrapper_uri: UriWrapper):
118-
"""Add a wrapper to the list of wrappers."""
119-
self.config.wrappers.append(wrapper_uri)
136+
def add_wrapper(self, uri: Uri, wrapper: Wrapper[UriPackageOrWrapper]):
137+
"""Add a wrapper by its URI to the builder's config"""
138+
self.config.wrappers[uri] = wrapper
120139
return self
121140

122-
def add_wrappers(self, wrappers_uris: List[UriWrapper]):
123-
"""Add a list of wrappers to the list of wrappers."""
124-
for wrapper_uri in wrappers_uris:
125-
self.add_wrapper(wrapper_uri)
141+
def add_wrappers(self, uri_wrappers: List[UriWrapper[UriPackageOrWrapper]]):
142+
"""Add a list of URI-wrapper pairs to the builder's config."""
143+
for uri_wrapper in uri_wrappers:
144+
self.add_wrapper(cast(Uri, uri_wrapper), uri_wrapper.wrapper)
126145
return self
127146

128-
def remove_wrapper(self, wrapper_uri: UriWrapper):
129-
"""Remove a wrapper from the list of wrappers."""
130-
self.config.wrappers.remove(wrapper_uri)
147+
def remove_wrapper(self, uri: Uri):
148+
"""Remove a wrapper by its URI from the builder's config."""
149+
del self.config.wrappers[uri]
131150
return self
132151

133-
def set_package(self, uri_package: UriPackage):
134-
"""Set the package in the builder's config, overiding any existing values."""
135-
self.config.packages = [uri_package]
152+
def remove_wrappers(self, uris: List[Uri]):
153+
"""Remove a list of wrappers by its URIs"""
154+
for uri in uris:
155+
self.remove_wrapper(uri)
136156
return self
137157

138-
def add_package(self, uri_package: UriPackage):
139-
"""Add a package to the list of packages."""
140-
self.config.packages.append(uri_package)
158+
def add_package(self, uri: Uri, package: WrapPackage[UriPackageOrWrapper]):
159+
"""Add a package by its URI to the builder's config."""
160+
self.config.packages[uri] = package
141161
return self
142162

143-
def add_packages(self, uri_packages: List[UriPackage]):
144-
"""Add a list of packages to the list of packages."""
163+
def add_packages(self, uri_packages: List[UriPackage[UriPackageOrWrapper]]):
164+
"""Add a list of URI-package pairs to the builder's config."""
145165
for uri_package in uri_packages:
146-
self.add_package(uri_package)
166+
self.add_package(cast(Uri, uri_package), uri_package.package)
147167
return self
148168

149-
def remove_package(self, uri_package: UriPackage):
150-
"""Remove a package from the list of packages."""
151-
self.config.packages.remove(uri_package)
169+
def remove_package(self, uri: Uri):
170+
"""Remove a package by its URI from the builder's config."""
171+
del self.config.packages[uri]
152172
return self
153173

154-
def set_resolver(self, uri_resolver: UriResolverLike):
155-
"""Set a single resolver for the `ClientConfig` object."""
156-
self.config.resolver = [uri_resolver]
174+
def remove_packages(self, uris: List[Uri]):
175+
"""Remove a list of packages by its URIs from the builder's config."""
176+
for uri in uris:
177+
self.remove_package(uri)
157178
return self
158179

159-
def add_resolver(self, resolver: UriResolverLike):
160-
"""Add a resolver to the list of resolvers."""
161-
if self.config.resolver is None:
162-
raise ValueError(
163-
"This resolver is not set. Please set a resolver before adding resolvers."
164-
)
165-
self.config.resolver.append(resolver)
180+
def add_resolver(self, resolver: UriResolver):
181+
"""Add a resolver to the builder's config."""
182+
self.config.resolvers.append(resolver)
166183
return self
167184

168-
def add_resolvers(self, resolvers_list: List[UriResolverLike]):
169-
"""Add a list of resolvers to the list of resolvers."""
185+
def add_resolvers(self, resolvers_list: List[UriResolver]):
186+
"""Add a list of resolvers to the builder's config."""
170187
for resolver in resolvers_list:
171188
self.add_resolver(resolver)
172189
return self
173190

174-
def set_uri_redirect(self, uri_from: Uri, uri_to: Uri):
175-
"""
176-
Set an uri redirect, from one uri to another.
177-
178-
If there was a redirect previously listed, it's changed to the new one.
179-
"""
180-
self.config.redirects[uri_from] = uri_to
191+
def add_redirect(self, from_uri: Uri, to_uri: Uri):
192+
"""Add a URI redirect from `from_uri` to `to_uri`."""
193+
self.config.redirects[from_uri] = to_uri
181194
return self
182195

183-
def remove_uri_redirect(self, uri_from: Uri):
184-
"""Remove an uri redirect, from one uri to another."""
185-
self.config.redirects.pop(uri_from)
196+
def remove_redirect(self, from_uri: Uri):
197+
"""Remove a URI redirect by `from_uri`."""
198+
del self.config.redirects[from_uri]
186199
return self
187200

188-
def set_uri_redirects(self, redirects: List[Dict[Uri, Uri]]):
189-
"""Set various Uri redirects from a list simultaneously."""
190-
count = 0
191-
for redir in redirects:
192-
for key, value in redir.items():
193-
self.set_uri_redirect(key, value)
194-
count += 1
201+
def add_redirects(self, redirects: Dict[Uri, Uri]):
202+
"""Add a list of URI redirects to the builder's config."""
203+
self.config.redirects.update(redirects)
195204
return self
196-
197-
198-
class ClientConfigBuilder(BaseClientConfigBuilder):
199-
"""
200-
A class that can build the `ClientConfig` object.
201-
202-
This class inherits the `BaseClientConfigBuilder` class,
203-
and adds the `build` method
204-
"""
205-
206-
def build(self) -> ClientConfig:
207-
"""Return a sanitized config object from the builder's config."""
208-
return self.config
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .builder_config import *
2+
from .build_options import *
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from dataclasses import dataclass
2+
from typing import Optional
3+
4+
from polywrap_core import UriResolver
5+
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

0 commit comments

Comments
 (0)