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 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
+ ]
@@ -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
@@ -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())