pkl-python 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pkl/__init__.py +118 -0
- pkl/evaluator_manager.py +384 -0
- pkl/evaluator_options.py +169 -0
- pkl/msgapi.py +312 -0
- pkl/parser.py +229 -0
- pkl/reader.py +64 -0
- pkl/server.py +156 -0
- pkl/utils.py +33 -0
- pkl_python-0.0.0.dist-info/LICENSE +21 -0
- pkl_python-0.0.0.dist-info/METADATA +168 -0
- pkl_python-0.0.0.dist-info/RECORD +14 -0
- pkl_python-0.0.0.dist-info/WHEEL +5 -0
- pkl_python-0.0.0.dist-info/entry_points.txt +2 -0
- pkl_python-0.0.0.dist-info/top_level.txt +1 -0
pkl/__init__.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
from urllib.parse import ParseResult, urlparse
|
|
5
|
+
|
|
6
|
+
from pkl.evaluator_manager import Evaluator, EvaluatorManager
|
|
7
|
+
from pkl.evaluator_options import EvaluatorOptions, PreconfiguredOptions
|
|
8
|
+
from pkl.parser import DataSize, Duration, IntSeq, Pair, Parser, Regex
|
|
9
|
+
from pkl.reader import ModuleReader, PathElement, ResourceReader
|
|
10
|
+
from pkl.utils import ModuleSource, PklBugError, PklError
|
|
11
|
+
|
|
12
|
+
# get version
|
|
13
|
+
with open(os.path.join(os.path.dirname(__file__), "VERSION"), "r") as _f:
|
|
14
|
+
__version__ = _f.read().strip()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PklDefaultType:
|
|
18
|
+
def __repr__(self):
|
|
19
|
+
return "<PklDefault>"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
PKL_DEFAULT = PklDefaultType()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _search_project_dir(module_path: str) -> str:
|
|
26
|
+
cur_path = Path(module_path).parent.absolute()
|
|
27
|
+
while not (cur_path / "PklProject").exists():
|
|
28
|
+
cur_path = cur_path.parent
|
|
29
|
+
if str(cur_path) == "/":
|
|
30
|
+
break
|
|
31
|
+
|
|
32
|
+
if str(cur_path) == "/":
|
|
33
|
+
cur_path = Path(module_path).parent
|
|
34
|
+
return str(cur_path.absolute())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load(
|
|
38
|
+
module_uri: Union[str, Path],
|
|
39
|
+
*,
|
|
40
|
+
module_text: Optional[str] = None,
|
|
41
|
+
expr: Optional[str] = None,
|
|
42
|
+
project_dir: str = PKL_DEFAULT,
|
|
43
|
+
evaluator_options: EvaluatorOptions = PreconfiguredOptions(),
|
|
44
|
+
parser=None,
|
|
45
|
+
debug=False,
|
|
46
|
+
**kwargs,
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Loads and evaluates a Pkl module or expression with specified parameters and customization options.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
module_uri (str): The absolute URI of the module to be loaded.
|
|
53
|
+
module_text (Optional[str], None): Optionally, the content of the module to be loaded.
|
|
54
|
+
If None, the module is loaded from the specified URI.
|
|
55
|
+
expr (Optional[str], None): Optionally, a Pkl expression to be evaluated
|
|
56
|
+
within the loaded module. If None, the entire module is evaluated.
|
|
57
|
+
project_dir (str, PKL_DEFAULT): The project directory to use for this command.
|
|
58
|
+
By default, searches up from the working directory for a PklProject file.
|
|
59
|
+
evaluator_options (EvaluatorOptions, PreconfiguredOptions()):
|
|
60
|
+
extra options for evaluator
|
|
61
|
+
parser: A specific parser to be used for parsing the module.
|
|
62
|
+
If None, a default parser is used.
|
|
63
|
+
debug (bool, False): Enable debugging mode for additional output and diagnostics.
|
|
64
|
+
**kwargs: Additional keyword arguments for extensibility and future use.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The result of the module or expression evaluation, depending on the inputs and configuration.
|
|
68
|
+
|
|
69
|
+
This function provides a flexible interface for loading and evaluating Pkl modules
|
|
70
|
+
with a variety of customization options, including custom module and resource readers,
|
|
71
|
+
environmental configurations, and support for complex project dependencies.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
parsed = urlparse(str(module_uri))
|
|
75
|
+
|
|
76
|
+
def is_uri(_uri: ParseResult):
|
|
77
|
+
return bool(_uri.scheme) and (bool(_uri.netloc) or bool(_uri.path))
|
|
78
|
+
|
|
79
|
+
if module_text:
|
|
80
|
+
source = ModuleSource.from_text(module_text)
|
|
81
|
+
elif is_uri(parsed):
|
|
82
|
+
source = ModuleSource.from_uri(module_uri)
|
|
83
|
+
else:
|
|
84
|
+
source = ModuleSource.from_path(module_uri)
|
|
85
|
+
|
|
86
|
+
if project_dir is PKL_DEFAULT:
|
|
87
|
+
project_dir = _search_project_dir(str(module_uri))
|
|
88
|
+
|
|
89
|
+
with EvaluatorManager(debug=debug) as manager:
|
|
90
|
+
if (Path(project_dir) / "PklProject").exists():
|
|
91
|
+
evaluator = manager.new_project_evaluator(
|
|
92
|
+
project_dir, evaluator_options, parser=parser
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
evaluator = manager.new_evaluator(evaluator_options, parser=parser)
|
|
96
|
+
config = evaluator.evaluate_expression(source, expr)
|
|
97
|
+
return config
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = [
|
|
101
|
+
"load",
|
|
102
|
+
"Evaluator",
|
|
103
|
+
"EvaluatorManager",
|
|
104
|
+
"EvaluatorOptions",
|
|
105
|
+
"PreconfiguredOptions",
|
|
106
|
+
"ModuleReader",
|
|
107
|
+
"ResourceReader",
|
|
108
|
+
"PathElement",
|
|
109
|
+
"Parser",
|
|
110
|
+
"ModuleSource",
|
|
111
|
+
"PklError",
|
|
112
|
+
"PklBugError",
|
|
113
|
+
"Duration",
|
|
114
|
+
"DataSize",
|
|
115
|
+
"Pair",
|
|
116
|
+
"IntSeq",
|
|
117
|
+
"Regex",
|
|
118
|
+
]
|
pkl/evaluator_manager.py
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from dataclasses import asdict
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, List, Optional, Union
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
import msgpack
|
|
8
|
+
|
|
9
|
+
from pkl.evaluator_options import (
|
|
10
|
+
Checksums,
|
|
11
|
+
EvaluatorOptions,
|
|
12
|
+
PreconfiguredOptions,
|
|
13
|
+
RemoteDependency,
|
|
14
|
+
)
|
|
15
|
+
from pkl.msgapi import (
|
|
16
|
+
CloseEvaluator,
|
|
17
|
+
CreateEvaluator,
|
|
18
|
+
CreateEvaluatorResponse,
|
|
19
|
+
EvaluateRequest,
|
|
20
|
+
EvaluateResponse,
|
|
21
|
+
EvaluatorListModulesRequest,
|
|
22
|
+
EvaluatorListModulesResponse,
|
|
23
|
+
EvaluatorListResourcesRequest,
|
|
24
|
+
EvaluatorListResourcesResponse,
|
|
25
|
+
EvaluatorReadModuleRequest,
|
|
26
|
+
EvaluatorReadModuleResponse,
|
|
27
|
+
EvaluatorReadResourceRequest,
|
|
28
|
+
EvaluatorReadResourceResponse,
|
|
29
|
+
IncomingMessage,
|
|
30
|
+
Log,
|
|
31
|
+
OutgoingMessage,
|
|
32
|
+
Project,
|
|
33
|
+
)
|
|
34
|
+
from pkl.parser import Parser
|
|
35
|
+
from pkl.reader import ModuleReader, ResourceReader
|
|
36
|
+
from pkl.server import PKLServer
|
|
37
|
+
from pkl.utils import ModuleSource, PklBugError, PklError
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Evaluator:
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
evaluatorId: int,
|
|
44
|
+
prev_requestId: int,
|
|
45
|
+
manager: "EvaluatorManager",
|
|
46
|
+
resource_readers: Optional[List[ResourceReader]] = None,
|
|
47
|
+
module_readers: Optional[List[ModuleReader]] = None,
|
|
48
|
+
*,
|
|
49
|
+
parser=None,
|
|
50
|
+
):
|
|
51
|
+
self.evaluatorId = evaluatorId
|
|
52
|
+
self.pending_requests = {}
|
|
53
|
+
self.closed = False
|
|
54
|
+
self._manager = manager
|
|
55
|
+
self._prev_requestId = prev_requestId
|
|
56
|
+
|
|
57
|
+
self.resource_readers = resource_readers or []
|
|
58
|
+
self.module_readers = module_readers or []
|
|
59
|
+
|
|
60
|
+
self.parser = parser or Parser()
|
|
61
|
+
|
|
62
|
+
def _get_requestId(self):
|
|
63
|
+
res = self._prev_requestId + 1
|
|
64
|
+
self._prev_requestId = res
|
|
65
|
+
return res
|
|
66
|
+
|
|
67
|
+
def handle_request(self, msg: IncomingMessage):
|
|
68
|
+
if isinstance(msg, EvaluateResponse):
|
|
69
|
+
self.handle_evaluate_response(msg)
|
|
70
|
+
elif isinstance(msg, EvaluatorReadModuleRequest):
|
|
71
|
+
self.handle_read_module(msg)
|
|
72
|
+
elif isinstance(msg, EvaluatorReadResourceRequest):
|
|
73
|
+
self.handle_read_resource(msg)
|
|
74
|
+
elif isinstance(msg, EvaluatorListModulesRequest):
|
|
75
|
+
self.handle_list_modules(msg)
|
|
76
|
+
elif isinstance(msg, EvaluatorListResourcesRequest):
|
|
77
|
+
self.handle_list_resources(msg)
|
|
78
|
+
elif isinstance(msg, Log):
|
|
79
|
+
self.handle_log(msg)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(f"Unhandled request: {msg}")
|
|
82
|
+
|
|
83
|
+
def evaluate_expression(self, source: ModuleSource, expr: Optional[str]):
|
|
84
|
+
binary_res = self._evaluate_expression_raw(source, expr)
|
|
85
|
+
decoded = msgpack.unpackb(binary_res, strict_map_key=False)
|
|
86
|
+
parsed = self.parser.parse(decoded)
|
|
87
|
+
return parsed
|
|
88
|
+
|
|
89
|
+
def _evaluate_expression_raw(self, source: ModuleSource, expr: Optional[str]):
|
|
90
|
+
if self.closed:
|
|
91
|
+
raise ValueError("Evaluator is closed")
|
|
92
|
+
|
|
93
|
+
requestId = self._get_requestId()
|
|
94
|
+
|
|
95
|
+
request = EvaluateRequest(
|
|
96
|
+
requestId=requestId,
|
|
97
|
+
evaluatorId=self.evaluatorId,
|
|
98
|
+
moduleUri=source.uri,
|
|
99
|
+
moduleText=source.text,
|
|
100
|
+
expr=expr,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self._manager.send(request)
|
|
104
|
+
response: EvaluateResponse = self._manager.receive(requestId)
|
|
105
|
+
|
|
106
|
+
if response.error is not None:
|
|
107
|
+
raise PklError("\n" + response.error)
|
|
108
|
+
return response.result
|
|
109
|
+
|
|
110
|
+
def evaluate_module(self, source: ModuleSource):
|
|
111
|
+
return self.evaluate_expression(source, None)
|
|
112
|
+
|
|
113
|
+
def evaluate_output_files(self, source: ModuleSource) -> List[str]:
|
|
114
|
+
return self.evaluate_expression(
|
|
115
|
+
source, "output.files.toMap().mapValues((_, it) -> it.text)"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def evaluate_output_text(self, source: ModuleSource) -> str:
|
|
119
|
+
return self.evaluate_expression(source, "output")
|
|
120
|
+
|
|
121
|
+
def evaluate_output_value(self, source: ModuleSource):
|
|
122
|
+
return self.evaluate_expression(source, "output.value")
|
|
123
|
+
|
|
124
|
+
def _find_module_reader(
|
|
125
|
+
self, msg: Union[EvaluatorReadModuleRequest, EvaluatorListModulesRequest]
|
|
126
|
+
):
|
|
127
|
+
uri = urlparse(msg.uri)
|
|
128
|
+
for reader in self.module_readers:
|
|
129
|
+
if reader.scheme == uri.scheme:
|
|
130
|
+
return reader, uri
|
|
131
|
+
raise ValueError(f"No module reader found for scheme '{uri.scheme}'")
|
|
132
|
+
|
|
133
|
+
def _find_resource_reader(
|
|
134
|
+
self, msg: Union[EvaluatorReadResourceRequest, EvaluatorListResourcesRequest]
|
|
135
|
+
):
|
|
136
|
+
uri = urlparse(msg.uri)
|
|
137
|
+
for reader in self.resource_readers:
|
|
138
|
+
if reader.scheme == uri.scheme:
|
|
139
|
+
return reader, uri
|
|
140
|
+
raise ValueError(f"No resource reader found for scheme '{uri.scheme}'")
|
|
141
|
+
|
|
142
|
+
def handle_log(self, msg: Log):
|
|
143
|
+
level_map = {
|
|
144
|
+
0: "TRACE",
|
|
145
|
+
1: "WARN",
|
|
146
|
+
}
|
|
147
|
+
s = f"pkl: {level_map[msg.level]}: {msg.message} ({msg.frameUri})"
|
|
148
|
+
if msg.level == 0:
|
|
149
|
+
print(s)
|
|
150
|
+
elif msg.level == 1:
|
|
151
|
+
warnings.warn(s)
|
|
152
|
+
|
|
153
|
+
def handle_evaluate_response(self, msg: EvaluateResponse):
|
|
154
|
+
raise NotImplementedError
|
|
155
|
+
|
|
156
|
+
def handle_read_resource(self, msg: EvaluatorReadResourceRequest):
|
|
157
|
+
reader, uri = self._find_resource_reader(msg)
|
|
158
|
+
try:
|
|
159
|
+
contents = reader.read(uri.geturl())
|
|
160
|
+
error = None
|
|
161
|
+
except Exception as e:
|
|
162
|
+
contents = None
|
|
163
|
+
error = str(e)
|
|
164
|
+
response = EvaluatorReadResourceResponse(
|
|
165
|
+
msg.requestId, msg.evaluatorId, contents, error
|
|
166
|
+
)
|
|
167
|
+
self._manager.send(response)
|
|
168
|
+
|
|
169
|
+
def handle_read_module(self, msg: EvaluatorReadModuleRequest):
|
|
170
|
+
reader, uri = self._find_module_reader(msg)
|
|
171
|
+
try:
|
|
172
|
+
contents = reader.read(uri.geturl())
|
|
173
|
+
error = None
|
|
174
|
+
except Exception as e:
|
|
175
|
+
contents = None
|
|
176
|
+
error = str(e)
|
|
177
|
+
response = EvaluatorReadModuleResponse(
|
|
178
|
+
msg.requestId, msg.evaluatorId, contents, error
|
|
179
|
+
)
|
|
180
|
+
self._manager.send(response)
|
|
181
|
+
|
|
182
|
+
def handle_list_resources(self, msg: EvaluatorListResourcesRequest):
|
|
183
|
+
reader, uri = self._find_resource_reader(msg)
|
|
184
|
+
try:
|
|
185
|
+
elements = reader.list_elements(uri.geturl())
|
|
186
|
+
error = None
|
|
187
|
+
except Exception as e:
|
|
188
|
+
elements = None
|
|
189
|
+
error = str(e)
|
|
190
|
+
response = EvaluatorListResourcesResponse(
|
|
191
|
+
msg.requestId, msg.evaluatorId, elements, error
|
|
192
|
+
)
|
|
193
|
+
self._manager.send(response)
|
|
194
|
+
|
|
195
|
+
def handle_list_modules(self, msg: EvaluatorListModulesRequest):
|
|
196
|
+
reader, uri = self._find_module_reader(msg)
|
|
197
|
+
try:
|
|
198
|
+
elements = reader.list_elements(uri.geturl())
|
|
199
|
+
error = None
|
|
200
|
+
except Exception as e:
|
|
201
|
+
elements = None
|
|
202
|
+
error = str(e)
|
|
203
|
+
response = EvaluatorListModulesResponse(
|
|
204
|
+
msg.requestId, msg.evaluatorId, elements, error
|
|
205
|
+
)
|
|
206
|
+
self._manager.send(response)
|
|
207
|
+
|
|
208
|
+
def close(self):
|
|
209
|
+
request = CloseEvaluator(self.evaluatorId)
|
|
210
|
+
self._manager.send(request)
|
|
211
|
+
|
|
212
|
+
def __enter__(self):
|
|
213
|
+
return self
|
|
214
|
+
|
|
215
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
216
|
+
self.close()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class EvaluatorManager:
|
|
220
|
+
def __init__(
|
|
221
|
+
self,
|
|
222
|
+
pkl_command: Optional[List[str]] = None,
|
|
223
|
+
*,
|
|
224
|
+
debug=False,
|
|
225
|
+
):
|
|
226
|
+
self._evaluators: Dict[int, Evaluator] = {}
|
|
227
|
+
self._closed = False
|
|
228
|
+
self._debug = debug
|
|
229
|
+
self._pkl_command = pkl_command
|
|
230
|
+
self._server = PKLServer(pkl_command, debug=debug)
|
|
231
|
+
|
|
232
|
+
self._prev_id = -100
|
|
233
|
+
|
|
234
|
+
def send(self, msg: OutgoingMessage):
|
|
235
|
+
obj = msg.to_json()
|
|
236
|
+
encoded = msgpack.packb(obj)
|
|
237
|
+
self._server.send(encoded)
|
|
238
|
+
self._server.receive_err()
|
|
239
|
+
|
|
240
|
+
def receive(self, requestId) -> IncomingMessage:
|
|
241
|
+
while True:
|
|
242
|
+
msg = self._server.receive()
|
|
243
|
+
self._server.receive_err()
|
|
244
|
+
decoded = IncomingMessage.decode(msg)
|
|
245
|
+
|
|
246
|
+
if hasattr(decoded, "requestId") and decoded.requestId == requestId:
|
|
247
|
+
return decoded
|
|
248
|
+
|
|
249
|
+
self._evaluators[decoded.evaluatorId].handle_request(decoded)
|
|
250
|
+
|
|
251
|
+
def _receive_create_response(self, requestId) -> CreateEvaluatorResponse:
|
|
252
|
+
while True:
|
|
253
|
+
msg = self._server.receive()
|
|
254
|
+
self._server.receive_err()
|
|
255
|
+
decoded = IncomingMessage.decode(msg)
|
|
256
|
+
|
|
257
|
+
if (
|
|
258
|
+
isinstance(decoded, CreateEvaluatorResponse)
|
|
259
|
+
and decoded.requestId == requestId
|
|
260
|
+
):
|
|
261
|
+
return decoded
|
|
262
|
+
|
|
263
|
+
# self._evaluators[decoded.evaluatorId].pending_requests[requestId] = decoded
|
|
264
|
+
self._evaluators[decoded.evaluatorId].handle_request(decoded)
|
|
265
|
+
|
|
266
|
+
def new_evaluator(
|
|
267
|
+
self, options: EvaluatorOptions, project: Optional[Project] = None, parser=None
|
|
268
|
+
):
|
|
269
|
+
if self._closed:
|
|
270
|
+
raise ValueError("Server closed")
|
|
271
|
+
|
|
272
|
+
requestId = self._get_start_requestId()
|
|
273
|
+
opt_dict = asdict(options)
|
|
274
|
+
|
|
275
|
+
opt_dict["clientModuleReaders"] = opt_dict["moduleReaders"]
|
|
276
|
+
opt_dict["clientResourceReaders"] = opt_dict["resourceReaders"]
|
|
277
|
+
del opt_dict["moduleReaders"]
|
|
278
|
+
del opt_dict["resourceReaders"]
|
|
279
|
+
|
|
280
|
+
create_evaluator = CreateEvaluator(
|
|
281
|
+
requestId=requestId, project=project, **opt_dict
|
|
282
|
+
)
|
|
283
|
+
self.send(create_evaluator)
|
|
284
|
+
response: CreateEvaluatorResponse = self._receive_create_response(requestId)
|
|
285
|
+
|
|
286
|
+
if response.error is not None:
|
|
287
|
+
raise PklBugError(response.error)
|
|
288
|
+
|
|
289
|
+
evaluator = Evaluator(
|
|
290
|
+
response.evaluatorId,
|
|
291
|
+
requestId,
|
|
292
|
+
self,
|
|
293
|
+
resource_readers=options.resourceReaders,
|
|
294
|
+
module_readers=options.moduleReaders,
|
|
295
|
+
parser=parser,
|
|
296
|
+
)
|
|
297
|
+
self._evaluators[response.evaluatorId] = evaluator
|
|
298
|
+
return evaluator
|
|
299
|
+
|
|
300
|
+
def new_project_evaluator(
|
|
301
|
+
self, project_dir: str, options: EvaluatorOptions, parser=None
|
|
302
|
+
):
|
|
303
|
+
project_evaluator = self.new_evaluator(PreconfiguredOptions(), parser=parser)
|
|
304
|
+
project = load_project_from_evaluator(project_evaluator, project_dir)
|
|
305
|
+
evaluator = self.new_evaluator(options, project, parser)
|
|
306
|
+
return evaluator
|
|
307
|
+
|
|
308
|
+
def _get_start_requestId(self):
|
|
309
|
+
res = self._prev_id + 100
|
|
310
|
+
self._prev_id = res
|
|
311
|
+
return res
|
|
312
|
+
|
|
313
|
+
def close(self):
|
|
314
|
+
self._server.terminate()
|
|
315
|
+
self._closed = True
|
|
316
|
+
|
|
317
|
+
def __enter__(self):
|
|
318
|
+
return self
|
|
319
|
+
|
|
320
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
321
|
+
self.close()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def new_evaluator_manager_with_command(pkl_command: List[str]):
|
|
325
|
+
with EvaluatorManager(pkl_command) as manager:
|
|
326
|
+
return manager
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def new_evaluator(options: EvaluatorOptions):
|
|
330
|
+
return new_evaluator_with_command(None, options)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def new_evaluator_with_command(
|
|
334
|
+
pkl_command: Optional[List[str]], options: EvaluatorOptions
|
|
335
|
+
):
|
|
336
|
+
with EvaluatorManager(pkl_command) as manager:
|
|
337
|
+
return manager.new_evaluator(options)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def new_project_evaluator(project_dir: str, options: EvaluatorOptions):
|
|
341
|
+
return new_project_evaluator_with_command(project_dir, None, options)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def new_project_evaluator_with_command(
|
|
345
|
+
project_dir: str, pkl_command: Optional[List[str]], options: EvaluatorOptions
|
|
346
|
+
):
|
|
347
|
+
with EvaluatorManager(pkl_command) as manager:
|
|
348
|
+
project_evaluator = manager.new_evaluator(PreconfiguredOptions())
|
|
349
|
+
project = load_project_from_evaluator(project_evaluator, project_dir)
|
|
350
|
+
return manager.new_evaluator(options, project)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def load_project(path) -> Project:
|
|
354
|
+
with EvaluatorManager() as manager:
|
|
355
|
+
evaluator = manager.new_evaluator(PreconfiguredOptions())
|
|
356
|
+
return load_project_from_evaluator(evaluator, path)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _encode_dependencies(
|
|
360
|
+
input_dependencies: Dict,
|
|
361
|
+
) -> Dict[str, Union[Project, RemoteDependency]]:
|
|
362
|
+
dependencies = {}
|
|
363
|
+
for k, v in input_dependencies.items():
|
|
364
|
+
if v.__class__.__name__ == "Project":
|
|
365
|
+
dependencies[k] = Project(
|
|
366
|
+
v.projectFileUri,
|
|
367
|
+
packageUri=v.package.uri,
|
|
368
|
+
dependencies=_encode_dependencies(v.dependencies),
|
|
369
|
+
)
|
|
370
|
+
elif v.__class__.__name__ == "RemoteDependency":
|
|
371
|
+
dependencies[k] = RemoteDependency(
|
|
372
|
+
packageUri=v.uri, checksums=Checksums(v.checksums.sha256)
|
|
373
|
+
)
|
|
374
|
+
else:
|
|
375
|
+
raise ValueError(f"Unknown dependency: {v.__class__.__name__}")
|
|
376
|
+
return dependencies
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def load_project_from_evaluator(evaluator: Evaluator, path) -> Project:
|
|
380
|
+
path = str(Path(path) / "PklProject")
|
|
381
|
+
config = evaluator.evaluate_output_value(ModuleSource.from_path(path))
|
|
382
|
+
dependencies = _encode_dependencies(config.dependencies)
|
|
383
|
+
project = Project(config.projectFileUri, dependencies=dependencies)
|
|
384
|
+
return project
|
pkl/evaluator_options.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Optional, Union
|
|
7
|
+
|
|
8
|
+
from pkl.reader import ModuleReader, ResourceReader
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ClientResourceReader:
|
|
13
|
+
# The URI scheme this reader is responsible for reading.
|
|
14
|
+
scheme: str
|
|
15
|
+
|
|
16
|
+
# Tells whether the path part of ths URI has a
|
|
17
|
+
# [hier-part](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
|
|
18
|
+
#
|
|
19
|
+
# An example of a hierarchical URI is `file:///path/to/my/file`, where
|
|
20
|
+
# `/path/to/my/file` designates a nested path through the `/` character.
|
|
21
|
+
#
|
|
22
|
+
# An example of a non-hierarchical URI is `pkl.base`, where the `base` does not denote
|
|
23
|
+
# any form of hierarchy.
|
|
24
|
+
hasHierarchicalUris: bool
|
|
25
|
+
|
|
26
|
+
# Tells whether this reader supports globbing.
|
|
27
|
+
isGlobbable: bool
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ClientModuleReader:
|
|
32
|
+
# The URI scheme this reader is responsible for reading.
|
|
33
|
+
scheme: str
|
|
34
|
+
|
|
35
|
+
# Tells whether the path part of ths URI has a
|
|
36
|
+
# [hier-part](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
|
|
37
|
+
#
|
|
38
|
+
# An example of a hierarchical URI is `file:///path/to/my/file`, where
|
|
39
|
+
# `/path/to/my/file` designates a nested path through the `/` character.
|
|
40
|
+
#
|
|
41
|
+
# An example of a non-hierarchical URI is `pkl.base`, where the `base` does not denote
|
|
42
|
+
# any form of hierarchy.
|
|
43
|
+
hasHierarchicalUris: bool
|
|
44
|
+
|
|
45
|
+
# Tells whether this reader supports globbing.
|
|
46
|
+
isGlobbable: bool
|
|
47
|
+
|
|
48
|
+
# Tells whether the module is local to the system.
|
|
49
|
+
#
|
|
50
|
+
# A local resource that [hasHierarchicalUris] supports triple-dot imports.
|
|
51
|
+
isLocal: bool
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class Checksums:
|
|
56
|
+
# The sha-256 checksum of this dependency's metadata.
|
|
57
|
+
sha256: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class RemoteDependency:
|
|
62
|
+
type: str = "remote"
|
|
63
|
+
|
|
64
|
+
# The canonical URI of this dependency
|
|
65
|
+
packageUri: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
# The checksums of this remote dependency
|
|
68
|
+
checksums: Optional[Checksums] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class Project:
|
|
73
|
+
# The URI pointing to the location of the project file.
|
|
74
|
+
projectFileUri: str
|
|
75
|
+
|
|
76
|
+
type: str = "local"
|
|
77
|
+
|
|
78
|
+
# The canonical URI of this project's package
|
|
79
|
+
packageUri: Optional[str] = None
|
|
80
|
+
|
|
81
|
+
# The dependencies of this project.
|
|
82
|
+
dependencies: Dict[str, Union[Project, RemoteDependency]] = field(
|
|
83
|
+
default_factory=dict
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class EvaluatorOptions:
|
|
89
|
+
# Regex patterns to determine which modules are allowed for import.
|
|
90
|
+
#
|
|
91
|
+
# API version of the CLI's `--allowed-modules` flag
|
|
92
|
+
allowedModules: Optional[List[str]] = None
|
|
93
|
+
|
|
94
|
+
# Regex patterns to dettermine which resources are allowed to be read.
|
|
95
|
+
#
|
|
96
|
+
# API version of the CLI's `--allowed-resources` flag
|
|
97
|
+
allowedResources: Optional[List[str]] = None
|
|
98
|
+
|
|
99
|
+
# Register client-side module readers.
|
|
100
|
+
moduleReaders: Optional[List[ModuleReader]] = None
|
|
101
|
+
|
|
102
|
+
# Register client-side resource readers.
|
|
103
|
+
resourceReaders: Optional[List[ResourceReader]] = None
|
|
104
|
+
|
|
105
|
+
# Directories, ZIP archives, or JAR archives
|
|
106
|
+
# to search when resolving `modulepath:` URIs.
|
|
107
|
+
#
|
|
108
|
+
# API version of the CLI's `--module-path` flag.
|
|
109
|
+
modulePaths: Optional[List[str]] = None
|
|
110
|
+
|
|
111
|
+
# Environment variable to set.
|
|
112
|
+
#
|
|
113
|
+
# API version of the CLI's `--env-var` flag.
|
|
114
|
+
env: Optional[Dict[str, str]] = None
|
|
115
|
+
|
|
116
|
+
# External properties to set.
|
|
117
|
+
#
|
|
118
|
+
# API version of the CLI's `--properties` flag.
|
|
119
|
+
properties: Optional[Dict[str, str]] = None
|
|
120
|
+
|
|
121
|
+
# Duration, in seconds, after which evaluation of a source module will be timed out.
|
|
122
|
+
#
|
|
123
|
+
# API version of the CLI's `--timeout` flag.
|
|
124
|
+
timeoutSeconds: Optional[int] = None
|
|
125
|
+
|
|
126
|
+
# Restricts access to file-based modules and resources to those located under the root directory.
|
|
127
|
+
rootDir: Optional[str] = None
|
|
128
|
+
|
|
129
|
+
# The cache directory for storing packages.
|
|
130
|
+
cacheDir: Optional[str] = None
|
|
131
|
+
|
|
132
|
+
# The format to generate.
|
|
133
|
+
#
|
|
134
|
+
# This sets the `pkl.outputFormat` external property.
|
|
135
|
+
outputFormat: Optional[str] = None
|
|
136
|
+
|
|
137
|
+
# The project dependency settings.
|
|
138
|
+
# project: Optional[Project] = None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class PreconfiguredOptions(EvaluatorOptions):
|
|
143
|
+
allowedModules: Optional[List[str]] = field(
|
|
144
|
+
default_factory=lambda: [
|
|
145
|
+
"pkl:",
|
|
146
|
+
"repl:",
|
|
147
|
+
"file:",
|
|
148
|
+
"http:",
|
|
149
|
+
"https:",
|
|
150
|
+
"modulepath:",
|
|
151
|
+
"package:",
|
|
152
|
+
"projectpackage:",
|
|
153
|
+
]
|
|
154
|
+
)
|
|
155
|
+
allowedResources: Optional[List[str]] = field(
|
|
156
|
+
default_factory=lambda: [
|
|
157
|
+
"http:",
|
|
158
|
+
"https:",
|
|
159
|
+
"file:",
|
|
160
|
+
"env:",
|
|
161
|
+
"prop:",
|
|
162
|
+
"modulepath:",
|
|
163
|
+
"package:",
|
|
164
|
+
"projectpackage:",
|
|
165
|
+
]
|
|
166
|
+
)
|
|
167
|
+
env: Optional[Dict[str, str]] = field(default_factory=lambda: dict(os.environ))
|
|
168
|
+
|
|
169
|
+
cacheDir: Optional[str] = str(Path("~/.pkl/cache").expanduser())
|