pkl-python 0.1.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/server.py ADDED
@@ -0,0 +1,191 @@
1
+ import atexit
2
+ import os
3
+ import platform
4
+ import select
5
+ import signal
6
+ import stat
7
+ import subprocess
8
+ import warnings
9
+ from pathlib import Path
10
+ from typing import List
11
+
12
+ import msgpack
13
+ import requests
14
+
15
+ BINARIES = {
16
+ ("darwin", "64bit"): "pkl-macos-amd64",
17
+ ("darwin", "aarch64"): "pkl-macos-aarch64",
18
+ ("linux", "64bit"): "pkl-linux-amd64",
19
+ ("linux", "aarch64"): "pkl-linux-aarch64",
20
+ ("linux", "64bit", "alpine"): "pkl-alpine-linux-amd64",
21
+ }
22
+ PKL_VERSION = "0.25.2"
23
+ BASE_PATH = "https://github.com/apple/pkl/releases/download/"
24
+
25
+
26
+ def preexec_function():
27
+ # Cause the child process to be terminated when the parent exits
28
+ signal.signal(signal.SIGHUP, signal.SIG_IGN)
29
+
30
+
31
+ def detect_system():
32
+ os_name = platform.system().lower()
33
+ arch, _ = platform.architecture()
34
+ return os_name, arch
35
+
36
+
37
+ def is_alpine_linux():
38
+ return os.path.isfile("/etc/alpine-release")
39
+
40
+
41
+ def execute_binary(binary_path):
42
+ subprocess.run([binary_path, "server"], check=True)
43
+
44
+
45
+ def download_binary(file, target_fp):
46
+ url = BASE_PATH + PKL_VERSION + "/" + file
47
+ response = requests.get(url)
48
+ with open(target_fp, "wb") as f:
49
+ f.write(response.content)
50
+
51
+
52
+ def get_binary_path():
53
+ os_name, arch = detect_system()
54
+ if os_name == "linux" and is_alpine_linux():
55
+ os_name = "alpine"
56
+ binary_key = (os_name, arch)
57
+ if binary_key == ("linux", "64bit") and is_alpine_linux():
58
+ binary_key += ("alpine",)
59
+ bin_file = BINARIES.get(binary_key)
60
+
61
+ if bin_file is None:
62
+ raise OSError("No compatible binary found for your system.")
63
+
64
+ bin_parent_path = (Path("~/.pkl/bin/") / PKL_VERSION).expanduser()
65
+ binary_path = bin_parent_path / bin_file
66
+
67
+ if not binary_path.exists():
68
+ binary_path.parent.mkdir(exist_ok=True, parents=True)
69
+ download_binary(bin_file, binary_path)
70
+
71
+ current_permissions = os.stat(binary_path).st_mode
72
+ new_permissions = current_permissions | stat.S_IXUSR
73
+ os.chmod(binary_path, new_permissions)
74
+
75
+ return binary_path
76
+
77
+
78
+ _PROCESSES = []
79
+
80
+
81
+ def terminate_processes():
82
+ for process in _PROCESSES:
83
+ process.terminate()
84
+ process.wait()
85
+
86
+
87
+ atexit.register(terminate_processes)
88
+
89
+
90
+ class PKLServer:
91
+ def __init__(self):
92
+ self._process: subprocess.Popen = None
93
+ self._cmd = [get_binary_path(), "server"]
94
+ self.next_request_id = 1
95
+
96
+ def get_request_id(self):
97
+ ret = self.next_request_id
98
+ self.next_request_id += 1
99
+ return ret
100
+
101
+ def start_process(self, debug=False):
102
+ if self._process:
103
+ return
104
+
105
+ env = os.environ.copy()
106
+ if debug:
107
+ env = {"PKL_DEBUG": "1"}
108
+
109
+ self._process = subprocess.Popen(
110
+ self._cmd,
111
+ stdin=subprocess.PIPE,
112
+ stdout=subprocess.PIPE,
113
+ stderr=subprocess.PIPE,
114
+ text=False,
115
+ bufsize=0,
116
+ preexec_fn=preexec_function,
117
+ env=env,
118
+ )
119
+ stdout_fd = self._process.stdout.fileno()
120
+ stderr_fd = self._process.stderr.fileno()
121
+ os.set_blocking(stdout_fd, False)
122
+ os.set_blocking(stderr_fd, False)
123
+ _PROCESSES.append(self._process)
124
+
125
+ def check_process(self, is_raise=False):
126
+ if self._process is None:
127
+ if is_raise:
128
+ raise ValueError("Start server first with 'start_process'")
129
+ return False
130
+ return True
131
+
132
+ def send_message(self, msg_obj):
133
+ self.check_process(is_raise=True)
134
+
135
+ encoded_message = msgpack.packb(msg_obj)
136
+ self._process.stdin.write(encoded_message)
137
+ self._process.stdin.flush()
138
+
139
+ def receive_message(self, timeout=None, empty_break=False) -> List:
140
+ self.check_process(is_raise=True)
141
+
142
+ stdout = self._process.stdout
143
+ stderr = self._process.stderr
144
+ outputs = []
145
+
146
+ while True:
147
+ readable, _, _ = select.select([stdout, stderr], [], [], timeout)
148
+ if empty_break and len(readable) == 0:
149
+ return
150
+
151
+ for stream in readable:
152
+ output = stream.read()
153
+
154
+ if stream == stdout:
155
+ outputs.append(output)
156
+ else: # stderr
157
+ print(output.decode(), end="")
158
+
159
+ if len(outputs) > 0:
160
+ break
161
+
162
+ # Check if the process has terminated
163
+ if self._process.poll() is not None:
164
+ print("Process has terminated")
165
+ break
166
+
167
+ assert len(outputs) == 1
168
+ unpacker = msgpack.Unpacker()
169
+ unpacker.feed(outputs[0])
170
+ responses = list(unpacker)
171
+ return responses
172
+
173
+ def receive_with_retry(self, max_retry=5) -> List:
174
+ response = None
175
+ retry = 0
176
+ while response is None and retry <= max_retry:
177
+ response = self.receive_message()
178
+ retry += 1
179
+ if retry == max_retry:
180
+ warnings.warn("Max retry reached.")
181
+ return response
182
+
183
+ def send_and_receive(self, msg_obj) -> List:
184
+ self.send_message(msg_obj)
185
+ response = self.receive_message()
186
+ return response
187
+
188
+ def terminate(self):
189
+ self._process.terminate()
190
+ self._process.wait()
191
+ self._process = None
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jungwoo Yang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.1
2
+ Name: pkl-python
3
+ Version: 0.1.0
4
+ Summary: Python library for Apple's PKL.
5
+ Author-email: Jungwoo Yang <jwyang0213@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Jungwoo Yang
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/jw-y/pkl
29
+ Project-URL: Bug Reports, https://github.com/jw-y/pkl/issues
30
+ Project-URL: Source, https://github.com/jw-y/pkl
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Requires-Python: >=3.6
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Requires-Dist: msgpack >=1.0.8
38
+ Provides-Extra: dev
39
+ Requires-Dist: pre-commit ; extra == 'dev'
40
+ Requires-Dist: black ; extra == 'dev'
41
+ Requires-Dist: isort ; extra == 'dev'
42
+ Requires-Dist: mypy ; extra == 'dev'
43
+ Requires-Dist: pylint ; extra == 'dev'
44
+ Requires-Dist: pytest ; extra == 'dev'
45
+ Requires-Dist: pytest-cov ; extra == 'dev'
46
+ Requires-Dist: tox ; extra == 'dev'
47
+
48
+ # PKLL - PKL Language Python Binding
49
+ Python binding for [Apple's Pkl language](https://pkl-lang.org/index.html).
50
+
51
+ ### Status
52
+ * Evaluator API: fully functional
53
+ * Code Generation: in development
54
+
55
+ ### TODO
56
+ * [ ] (codgen) binary installation feature
57
+ * [ ] (codgen) fix class order
58
+ * [ ] (codgen) clean up code
59
+
60
+ ## Installation
61
+
62
+ ``` bash
63
+ pip install pkll
64
+ ```
65
+
66
+ ## Usage
67
+ ### Basic Usage
68
+ Here's how you can start using PKLL to load a PKL module:
69
+
70
+ ```python
71
+ import pkll
72
+
73
+ config = pkll.load("path/to/pkl/example_module.pkl")
74
+ print(config)
75
+ ```
76
+
77
+ ## Advanced Features
78
+ For details on the parameters, refer [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
79
+
80
+ ```python
81
+ from pkll import load
82
+
83
+ # Advanced loading with custom environment and properties
84
+ result = load(
85
+ "path/to/pkl/example_module.pkl"
86
+ env={"CUSTOM_ENV": "value"},
87
+ properties={"custom.property": "value"}
88
+ )
89
+ print(result)
90
+ ```
91
+
92
+ ### Custom Handler
93
+ It is possible to add custom resources or module handler:
94
+ ```python
95
+ import pkll
96
+ from pkll.handler import (
97
+ ListResponse,
98
+ ReadModuleResponse,
99
+ ReadResourceResponse,
100
+ ResourcesHandler,
101
+ )
102
+ from pkll.msgapi import ClientResourceReader
103
+
104
+ class CustomModuleHandler(ResourcesHandler):
105
+ def list_response(self, uri: str) -> ListResponse:
106
+ return ListResponse(
107
+ pathElements=[{"name": "foo.pkl", "isDirectory": False}]
108
+ )
109
+
110
+ def read_response(self, uri: str) -> ReadResourceResponse:
111
+ return ReadModuleResponse(
112
+ contents="foo = 1",
113
+ )
114
+
115
+ config = pkll.load(
116
+ "./tests/myModule.pkl",
117
+ allowedModules=["pkl:", "repl:", "file:", "customfs:"],
118
+ clientModuleReaders=[
119
+ {
120
+ "scheme": "customfs",
121
+ "hasHierarchicalUris": True,
122
+ "isGlobbable": True,
123
+ "isLocal": True,
124
+ }
125
+ ],
126
+ debug=True,
127
+ module_handler=CustomModuleHandler(),
128
+ )
129
+ ```
130
+
131
+ ## Contributing
132
+ Contributions are welcome! If you'd like to contribute, please fork the repository and submit a pull request. For major changes, please open an issue first to discuss what you would like to change.
133
+
134
+ ## License
135
+ PKLL is released under the MIT License. See the LICENSE file for more details.
136
+
137
+ ## Contact
138
+ For support or to contribute, please contact jwyang0213@gmail.com or visit our GitHub repository to report issues or submit pull requests.
@@ -0,0 +1,31 @@
1
+ archive/cmd/pkl-gen-go/pkl-gen-python.py,sha256=Y7uiZRWrH4qLhZ6ikl-P_Y4pKYtVZBw89uzN9WeFuro,4541
2
+ archive/cmd/pkl-gen-python/pkl-gen-python.py,sha256=kVmPt5q3nSWGzbzA0WMuDdB2Rz83sFUpWzbQpQBcjEg,7077
3
+ old/pkl-gen-python/generate.py,sha256=XZZqEpTWLteP8rt6z80Z_Qwrsagc0WXVQWFYzfalZ_Q,3769
4
+ old/pkl-gen-python/main.py,sha256=HUsSaPE8uzR_Z4YbvscWsS-9i6hz3U3iDYHd6dtWwRc,2638
5
+ out/ExtendModule_pkl.py,sha256=i4aoOx5c5c3ASjAztdE-erlWK_G7kzLL5soN3ZupgBs,891
6
+ out/ExtendingOpenClass_pkl.py,sha256=QpkTYHvNA7EC02WXndOBmG0lYCzlEmihsf97xs-GOys,1594
7
+ out/Foo_pkl.py,sha256=WPBK3Oulsbu_nh2gSipc_Q1y7AZ4FsVpMFgXn-Qr55o,1641
8
+ out/Imports_pkl.py,sha256=DMSQLpfKv5M_HL-BdwsNrTfyjYr32EXuTjQ6iGTDYMk,574
9
+ out/MyModule_pkl.py,sha256=s7afVaFY0Z-MIDCePYJO7TaMWF3Rf0gQYxRXVf-CMZA,292
10
+ out/com_example_ExtendedSimple_pkl.py,sha256=RSzZ8iAkOkdf1GLBUOZIO5TxMny3Vidc5V0G9NntVoU,1092
11
+ out/com_example_Simple_pkl.py,sha256=YIr2S15YxjMIhIDhK5QV2SJtEtIoYnuMRwE7axLURYo,1631
12
+ out/lib3_pkl.py,sha256=-vlFnXzn_i8lwwHwo0VGeJyzEl9Xwx6XRjOkGugRZ9M,652
13
+ out/union_pkl.py,sha256=JDfYrRhSR7tksJyeeWs1FzmbvDjxjIyUU3gsbDmvA7A,922
14
+ pkl/VERSION,sha256=6d2FB_S_DG9CRY5BrqgzrQvT9hJycjNe7pv01YVB7Wc,6
15
+ pkl/__init__.py,sha256=xD15PQ7-zsRbWhp0GqyNCv9JcNFF_yH0T01iSVhRoew,6929
16
+ pkl/evaluator.py,sha256=AZvcNgckMc-5Jld-d4EjBSqSC9ZRI-M7iKprtMl1RsQ,8882
17
+ pkl/handler.py,sha256=SOvkb2Y0J0w0uKu4VJTOLr0f-VaFW3sdy2yUzLTAP0Q,823
18
+ pkl/msgapi.py,sha256=PCnPQ64rcwl7GDS80wW-6IC9ZN5Kkv2nuO5Zavlj0CQ,6812
19
+ pkl/parser.py,sha256=lOGJTWoSnR0vxrBcTKxSiiJRBLLAWpsi22wv_NBA06M,8637
20
+ pkl/server.py,sha256=DHYJGzcL86_gjuYq3WT2lsCs0SDaRzKCJOIAlNZeGKM,5214
21
+ scripts/download_binary.py,sha256=iLuL64ZJZqszCW6vQC6looxNm6rkeqzT3JB6essP5ME,978
22
+ scripts/eval.py,sha256=icuXL3Brf1I26GkiWzAbNR2sxwIG97_XGVm6n7rBPa4,917
23
+ tests/test_evaluator.py,sha256=qCMB0bui9SnSOSLfR43f0NHwh1CDzVRIbWx8p_DJqrM,123
24
+ tests/test_parser.py,sha256=qPwsoxC_k2nDqzC6mEE9_s8-9ZNlmuoGwZRWEqaIfR8,975
25
+ tests/test_responses.py,sha256=hD1KbEvq6WdsWLG3QNdujhhmdTQnAfcOnYjjVhug-a4,2855
26
+ tests/test_server.py,sha256=pZapHwM7N33f-1rG65m9eip-2jcv9uD-pUAzUbBESHc,129
27
+ pkl_python-0.1.0.dist-info/LICENSE,sha256=xakNlyP5yr5S03TW66xSAFmKjgc3eC4Dxz0ggbmFzFM,1069
28
+ pkl_python-0.1.0.dist-info/METADATA,sha256=q75Nqyf-S0ExJXdNuz04FyiroaZd6k_tN5ejIAcAP6M,4522
29
+ pkl_python-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
30
+ pkl_python-0.1.0.dist-info/top_level.txt,sha256=SmMRZ3XJJ2x8VtiVSRQV2E7lEKo3aBGYvkd0IKG6Rjw,34
31
+ pkl_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,6 @@
1
+ archive
2
+ old
3
+ out
4
+ pkl
5
+ scripts
6
+ tests
@@ -0,0 +1,42 @@
1
+ import argparse
2
+ from pathlib import Path
3
+
4
+ import requests
5
+
6
+ VERSION = "0.25.2"
7
+
8
+ BASE_PATH = "https://github.com/apple/pkl/releases/download/"
9
+ filenames = [
10
+ "pkl-macos-aarch64",
11
+ "pkl-macos-amd64",
12
+ "pkl-linux-aarch64",
13
+ "pkl-linux-amd64",
14
+ "pkl-alpine-linux-amd64",
15
+ ]
16
+ JAVA_FILE = f"https://repo1.maven.org/maven2/org/pkl-lang/pkl-cli-java/{VERSION}/pkl-cli-java-{VERSION}.jar"
17
+
18
+
19
+ def main(save_path):
20
+ urls = [BASE_PATH + VERSION + "/" + file for file in filenames]
21
+ urls.append(JAVA_FILE)
22
+
23
+ for url in urls:
24
+ file = url.split("/")[-1]
25
+ fp = save_path / file
26
+
27
+ response = requests.get(url)
28
+ with open(fp, "wb") as f:
29
+ f.write(response.content)
30
+
31
+ print("written to", fp)
32
+
33
+
34
+ if __name__ == "__main__":
35
+ parser = argparse.ArgumentParser()
36
+ parser.add_argument(
37
+ "output_path", help="Directory where binaries will be downloaded"
38
+ )
39
+
40
+ args = parser.parse_args()
41
+
42
+ main(Path(args.output_path))
scripts/eval.py ADDED
@@ -0,0 +1,32 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ from pprint import pprint
4
+
5
+ import pkll
6
+
7
+
8
+ def main(target, force=False, debug=False, is_print=False):
9
+ target = Path(target)
10
+
11
+ if target.is_file():
12
+ files = [target]
13
+ elif target.is_dir():
14
+ files = list(target.glob("*.pkl"))
15
+ else:
16
+ raise ValueError(target)
17
+
18
+ for f in files:
19
+ if f.is_file():
20
+ config = pkll.load(f.absolute().as_uri(), force_render=force, debug=debug)
21
+ if is_print:
22
+ pprint(config)
23
+
24
+
25
+ if __name__ == "__main__":
26
+ parser = argparse.ArgumentParser()
27
+ parser.add_argument("target")
28
+ parser.add_argument("--force", "-f", action="store_true")
29
+ parser.add_argument("--debug", "-d", action="store_true")
30
+ parser.add_argument("--print", "-p", action="store_true")
31
+ args = parser.parse_args()
32
+ main(args.target, force=args.force, debug=args.debug, is_print=args.print)
@@ -0,0 +1,7 @@
1
+ from pkl import Evaluator
2
+
3
+
4
+ def test_evaluator():
5
+ evaluator = Evaluator()
6
+ evaluator.create()
7
+ evaluator.close()
tests/test_parser.py ADDED
@@ -0,0 +1,58 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ import pkl
6
+
7
+ base_path = Path("./tests/Fixtures")
8
+
9
+
10
+ def test_AnyType():
11
+ file = base_path / "AnyType.pkl"
12
+ _ = pkl.load(file)
13
+
14
+
15
+ def test_ApiTypes():
16
+ file = base_path / "ApiTypes.pkl"
17
+ _ = pkl.load(file)
18
+
19
+
20
+ def test_Classes():
21
+ file = base_path / "Classes.pkl"
22
+ _ = pkl.load(file)
23
+
24
+
25
+ def test_Collections():
26
+ file = base_path / "Collections.pkl"
27
+ _ = pkl.load(file)
28
+
29
+
30
+ def test_ExtendedModule():
31
+ file = base_path / "ExtendedModule.pkl"
32
+ _ = pkl.load(file)
33
+
34
+
35
+ def test_OpenModule():
36
+ file = base_path / "OpenModule.pkl"
37
+ with pytest.raises(Exception):
38
+ _ = pkl.load(file)
39
+
40
+
41
+ def test_Poly():
42
+ file = base_path / "Poly.pkl"
43
+ _ = pkl.load(file)
44
+
45
+
46
+ def test_UnionTypes():
47
+ file = base_path / "UnionTypes.pkl"
48
+ _ = pkl.load(file)
49
+
50
+
51
+ def test_lib1():
52
+ file = base_path / "lib1.pkl"
53
+ _ = pkl.load(file)
54
+
55
+
56
+ def test_types():
57
+ file = Path("./tests") / "types.pkl"
58
+ _ = pkl.load(file)
@@ -0,0 +1,99 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ import pkl
6
+ from pkl.handler import (
7
+ ListResponse,
8
+ ReadModuleResponse,
9
+ ReadResourceResponse,
10
+ ResourcesHandler,
11
+ )
12
+ from pkl.msgapi import ClientResourceReader
13
+
14
+
15
+ def test_read_modules():
16
+ class CustomModuleHandler(ResourcesHandler):
17
+ def list_response(self, uri: str) -> ListResponse:
18
+ return ListResponse(
19
+ pathElements=[{"name": "foo.pkl", "isDirectory": False}]
20
+ )
21
+
22
+ def read_response(self, uri: str) -> ReadResourceResponse:
23
+ return ReadModuleResponse(
24
+ contents="foo = 1",
25
+ )
26
+
27
+ _ = pkl.load(
28
+ Path("./tests/myModule.pkl").absolute().as_uri(),
29
+ allowedModules=["pkl:", "repl:", "file:", "customfs:"],
30
+ clientModuleReaders=[
31
+ {
32
+ "scheme": "customfs",
33
+ "hasHierarchicalUris": True,
34
+ "isGlobbable": True,
35
+ "isLocal": True,
36
+ }
37
+ ],
38
+ debug=True,
39
+ module_handler=CustomModuleHandler(),
40
+ )
41
+
42
+
43
+ def test_read_modules_error():
44
+ class CustomModuleHandler(ResourcesHandler):
45
+ def list_response(self, uri: str) -> ListResponse:
46
+ return ListResponse(error="list error")
47
+
48
+ def read_response(self, uri: str) -> ReadResourceResponse:
49
+ return ReadModuleResponse(error="read module error")
50
+
51
+ with pytest.raises(Exception):
52
+ _ = pkl.load(
53
+ Path("./tests/myModule.pkl").absolute().as_uri(),
54
+ allowedModules=["pkl:", "repl:", "file:", "customfs:"],
55
+ clientModuleReaders=[
56
+ {
57
+ "scheme": "customfs",
58
+ "hasHierarchicalUris": True,
59
+ "isGlobbable": True,
60
+ "isLocal": True,
61
+ }
62
+ ],
63
+ module_handler=CustomModuleHandler(),
64
+ debug=True,
65
+ )
66
+
67
+
68
+ def test_read_resources():
69
+ class CustomResourceHandler(ResourcesHandler):
70
+ def list_response(self, uri: str) -> ListResponse:
71
+ return ListResponse(
72
+ pathElements=[{"name": "foo.txt", "isDirectory": False}]
73
+ )
74
+
75
+ def read_response(self, uri: str) -> ReadResourceResponse:
76
+ return ReadResourceResponse(
77
+ contents=b"Hello, World!",
78
+ )
79
+
80
+ _ = pkl.load(
81
+ Path("./tests/with_read.pkl").absolute().as_uri(),
82
+ allowedModules=[
83
+ "pkl:",
84
+ "repl:",
85
+ "file:",
86
+ ],
87
+ allowedResources=[
88
+ "customfs:",
89
+ ],
90
+ clientResourceReaders=[
91
+ ClientResourceReader(
92
+ scheme="customfs",
93
+ hasHierarchicalUris=True,
94
+ isGlobbable=True,
95
+ )
96
+ ],
97
+ debug=True,
98
+ resource_handler=CustomResourceHandler(),
99
+ )
tests/test_server.py ADDED
@@ -0,0 +1,7 @@
1
+ from pkl.server import PKLServer
2
+
3
+
4
+ def test_server():
5
+ server = PKLServer()
6
+ server.start_process()
7
+ server.terminate()