pkl-python 0.1.14__tar.gz → 0.1.16__tar.gz
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-python-0.1.14 → pkl_python-0.1.16}/PKG-INFO +9 -8
- {pkl-python-0.1.14 → pkl_python-0.1.16}/README.md +7 -6
- {pkl-python-0.1.14 → pkl_python-0.1.16}/pyproject.toml +13 -2
- pkl_python-0.1.16/src/pkl/VERSION +1 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/__init__.py +35 -15
- pkl_python-0.1.16/src/pkl/binary_manager.py +185 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/server.py +6 -62
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/PKG-INFO +9 -8
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/SOURCES.txt +2 -1
- pkl_python-0.1.16/tests/test_binary.py +61 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_load.py +5 -0
- pkl-python-0.1.14/setup.py +0 -74
- pkl-python-0.1.14/src/pkl/VERSION +0 -1
- {pkl-python-0.1.14 → pkl_python-0.1.16}/LICENSE +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/setup.cfg +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/evaluator_manager.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/evaluator_options.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/msgapi.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/parser.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/reader.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl/utils.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_gen_python.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/dependency_links.txt +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/entry_points.txt +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/requires.txt +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/src/pkl_python.egg-info/top_level.txt +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_evaluator_manager.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_fixtures.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_parser.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_readers.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_server.py +0 -0
- {pkl-python-0.1.14 → pkl_python-0.1.16}/tests/test_trace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pkl-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Python library for Apple's PKL.
|
|
5
5
|
Author-email: Jungwoo Yang <jwyang0213@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -31,7 +31,7 @@ Project-URL: Source, https://github.com/jw-y/pkl-python
|
|
|
31
31
|
Classifier: Programming Language :: Python :: 3
|
|
32
32
|
Classifier: License :: OSI Approved :: MIT License
|
|
33
33
|
Classifier: Operating System :: OS Independent
|
|
34
|
-
Requires-Python: >=3.
|
|
34
|
+
Requires-Python: >=3.8
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
License-File: LICENSE
|
|
37
37
|
Requires-Dist: msgpack>=1.0.8
|
|
@@ -68,6 +68,7 @@ Here's how you can start using `pkl-python` to load a PKL module:
|
|
|
68
68
|
import pkl
|
|
69
69
|
|
|
70
70
|
config = pkl.load("path/to/pkl/example_module.pkl")
|
|
71
|
+
config = pkl.loads("a: Int = 1 + 1")
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
### Code Generation
|
|
@@ -78,10 +79,6 @@ However, codegen lets you expect contents of pkl files within Python modules.
|
|
|
78
79
|
pkl-gen-python path/to/pkl/example_module.pkl
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
### Status
|
|
82
|
-
* Evaluator API: functional
|
|
83
|
-
* Code Generation: functional, but needs refining
|
|
84
|
-
|
|
85
82
|
### TODO
|
|
86
83
|
* [x] (codgen) pip binary installation
|
|
87
84
|
* [ ] (codgen) gatherer depth-first ordering
|
|
@@ -89,7 +86,6 @@ pkl-gen-python path/to/pkl/example_module.pkl
|
|
|
89
86
|
|
|
90
87
|
|
|
91
88
|
## Advanced Features
|
|
92
|
-
For details on the parameters, refer [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
93
89
|
|
|
94
90
|
```python
|
|
95
91
|
import pkl
|
|
@@ -100,6 +96,11 @@ config = pkl.load(None, module_text="a: Int = 1 + 1")
|
|
|
100
96
|
config = pkl.load("./tests/types.pkl", debug=True)
|
|
101
97
|
```
|
|
102
98
|
|
|
99
|
+
### `pkl.load` Parameters Detail
|
|
100
|
+
For details on the parameters, refer
|
|
101
|
+
* [`pkl eval`](https://pkl-lang.org/main/current/pkl-cli/index.html#command-eval)
|
|
102
|
+
* [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
103
|
+
|
|
103
104
|
### Custom Readers
|
|
104
105
|
It is possible to add module or resource or module readers:
|
|
105
106
|
```python
|
|
@@ -123,7 +124,7 @@ opts = PreconfiguredOptions(
|
|
|
123
124
|
moduleReaders=[TestModuleReader("customfs", True, True, True)]
|
|
124
125
|
)
|
|
125
126
|
opts.allowedModules.append("customfs:")
|
|
126
|
-
config = pkl.load("./tests/myModule.pkl", evaluator_options=opts)
|
|
127
|
+
config = pkl.load("./tests/pkls/myModule.pkl", evaluator_options=opts)
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
## Appendix
|
|
@@ -21,6 +21,7 @@ Here's how you can start using `pkl-python` to load a PKL module:
|
|
|
21
21
|
import pkl
|
|
22
22
|
|
|
23
23
|
config = pkl.load("path/to/pkl/example_module.pkl")
|
|
24
|
+
config = pkl.loads("a: Int = 1 + 1")
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
### Code Generation
|
|
@@ -31,10 +32,6 @@ However, codegen lets you expect contents of pkl files within Python modules.
|
|
|
31
32
|
pkl-gen-python path/to/pkl/example_module.pkl
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
### Status
|
|
35
|
-
* Evaluator API: functional
|
|
36
|
-
* Code Generation: functional, but needs refining
|
|
37
|
-
|
|
38
35
|
### TODO
|
|
39
36
|
* [x] (codgen) pip binary installation
|
|
40
37
|
* [ ] (codgen) gatherer depth-first ordering
|
|
@@ -42,7 +39,6 @@ pkl-gen-python path/to/pkl/example_module.pkl
|
|
|
42
39
|
|
|
43
40
|
|
|
44
41
|
## Advanced Features
|
|
45
|
-
For details on the parameters, refer [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
46
42
|
|
|
47
43
|
```python
|
|
48
44
|
import pkl
|
|
@@ -53,6 +49,11 @@ config = pkl.load(None, module_text="a: Int = 1 + 1")
|
|
|
53
49
|
config = pkl.load("./tests/types.pkl", debug=True)
|
|
54
50
|
```
|
|
55
51
|
|
|
52
|
+
### `pkl.load` Parameters Detail
|
|
53
|
+
For details on the parameters, refer
|
|
54
|
+
* [`pkl eval`](https://pkl-lang.org/main/current/pkl-cli/index.html#command-eval)
|
|
55
|
+
* [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
56
|
+
|
|
56
57
|
### Custom Readers
|
|
57
58
|
It is possible to add module or resource or module readers:
|
|
58
59
|
```python
|
|
@@ -76,7 +77,7 @@ opts = PreconfiguredOptions(
|
|
|
76
77
|
moduleReaders=[TestModuleReader("customfs", True, True, True)]
|
|
77
78
|
)
|
|
78
79
|
opts.allowedModules.append("customfs:")
|
|
79
|
-
config = pkl.load("./tests/myModule.pkl", evaluator_options=opts)
|
|
80
|
+
config = pkl.load("./tests/pkls/myModule.pkl", evaluator_options=opts)
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
## Appendix
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=42", "wheel"]
|
|
2
|
+
requires = ["setuptools>=42", "wheel", "msgpack", "requests"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -11,7 +11,7 @@ authors = [
|
|
|
11
11
|
]
|
|
12
12
|
license = { file = "LICENSE" }
|
|
13
13
|
readme = "README.md"
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Programming Language :: Python :: 3",
|
|
17
17
|
"License :: OSI Approved :: MIT License",
|
|
@@ -64,3 +64,14 @@ exclude = ["codegen/snippet-tests/output/*"]
|
|
|
64
64
|
|
|
65
65
|
[tool.pytest.ini_options]
|
|
66
66
|
addopts = "-ra -q"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
[tool.cibuildwheel]
|
|
70
|
+
build = [
|
|
71
|
+
"*-macosx_x86_64",
|
|
72
|
+
"*-macosx_arm64",
|
|
73
|
+
"*-manylinux_aarch64",
|
|
74
|
+
"*-manylinux_x86_64",
|
|
75
|
+
#"*-musllinux_x86_64",
|
|
76
|
+
"*-win_amd64",
|
|
77
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.16
|
|
@@ -14,23 +14,15 @@ with open(os.path.join(os.path.dirname(__file__), "VERSION"), "r") as _f:
|
|
|
14
14
|
__version__ = _f.read().strip()
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class PklDefaultType:
|
|
18
|
-
def __repr__(self):
|
|
19
|
-
return "<PklDefault>"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
PKL_DEFAULT = PklDefaultType()
|
|
23
|
-
|
|
24
|
-
|
|
25
17
|
def _search_project_dir(module_path: str) -> str:
|
|
26
18
|
cur_path = Path(module_path).parent.absolute()
|
|
19
|
+
root_path = cur_path.root
|
|
20
|
+
|
|
27
21
|
while not (cur_path / "PklProject").exists():
|
|
28
|
-
cur_path
|
|
29
|
-
if str(cur_path) == "/":
|
|
22
|
+
if cur_path == cur_path.parent or cur_path == root_path:
|
|
30
23
|
break
|
|
24
|
+
cur_path = cur_path.parent
|
|
31
25
|
|
|
32
|
-
if str(cur_path) == "/":
|
|
33
|
-
cur_path = Path(module_path).parent
|
|
34
26
|
return str(cur_path.absolute())
|
|
35
27
|
|
|
36
28
|
|
|
@@ -54,7 +46,7 @@ def load(
|
|
|
54
46
|
*,
|
|
55
47
|
module_text: Optional[str] = None,
|
|
56
48
|
expr: Optional[str] = None,
|
|
57
|
-
project_dir: str =
|
|
49
|
+
project_dir: Optional[str] = None,
|
|
58
50
|
evaluator_options: EvaluatorOptions = PreconfiguredOptions(),
|
|
59
51
|
parser=None,
|
|
60
52
|
debug=False,
|
|
@@ -69,7 +61,7 @@ def load(
|
|
|
69
61
|
If None, the module is loaded from the specified URI.
|
|
70
62
|
expr (Optional[str], None): Optionally, a Pkl expression to be evaluated
|
|
71
63
|
within the loaded module. If None, the entire module is evaluated.
|
|
72
|
-
project_dir (str,
|
|
64
|
+
project_dir (Optional[str], None): The project directory to use for this command.
|
|
73
65
|
By default, searches up from the working directory for a PklProject file.
|
|
74
66
|
evaluator_options (EvaluatorOptions, PreconfiguredOptions()):
|
|
75
67
|
extra options for evaluator
|
|
@@ -88,7 +80,7 @@ def load(
|
|
|
88
80
|
|
|
89
81
|
source = _parse_module_uri(module_uri, module_text)
|
|
90
82
|
|
|
91
|
-
if project_dir is
|
|
83
|
+
if project_dir is None:
|
|
92
84
|
project_dir = _search_project_dir(str(module_uri))
|
|
93
85
|
|
|
94
86
|
with EvaluatorManager(debug=debug) as manager:
|
|
@@ -102,8 +94,36 @@ def load(
|
|
|
102
94
|
return config
|
|
103
95
|
|
|
104
96
|
|
|
97
|
+
def loads(
|
|
98
|
+
module_text: Optional[str],
|
|
99
|
+
*,
|
|
100
|
+
expr: Optional[str] = None,
|
|
101
|
+
project_dir: Optional[str] = None,
|
|
102
|
+
evaluator_options: EvaluatorOptions = PreconfiguredOptions(),
|
|
103
|
+
parser=None,
|
|
104
|
+
debug=False,
|
|
105
|
+
**kwargs,
|
|
106
|
+
):
|
|
107
|
+
"""
|
|
108
|
+
This function is a specialized version of `load` that defaults `module_uri` to None.
|
|
109
|
+
|
|
110
|
+
{load.__doc__}
|
|
111
|
+
"""
|
|
112
|
+
return load(
|
|
113
|
+
module_uri=None,
|
|
114
|
+
module_text=module_text,
|
|
115
|
+
expr=expr,
|
|
116
|
+
project_dir=project_dir,
|
|
117
|
+
evaluator_options=evaluator_options,
|
|
118
|
+
parser=parser,
|
|
119
|
+
debug=debug,
|
|
120
|
+
**kwargs,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
105
124
|
__all__ = [
|
|
106
125
|
"load",
|
|
126
|
+
"loads",
|
|
107
127
|
"Evaluator",
|
|
108
128
|
"EvaluatorManager",
|
|
109
129
|
"EvaluatorOptions",
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
import stat
|
|
5
|
+
import subprocess
|
|
6
|
+
from functools import cache
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
ARM_BINARIES = {
|
|
12
|
+
"darwin": "pkl-macos-aarch64",
|
|
13
|
+
"linux": "pkl-linux-aarch64",
|
|
14
|
+
}
|
|
15
|
+
AMD64_BINARIES = {
|
|
16
|
+
"darwin": "pkl-macos-amd64",
|
|
17
|
+
"linux": "pkl-linux-amd64",
|
|
18
|
+
"alpine-linux": "pkl-alpine-linux-amd64",
|
|
19
|
+
"windows": "pkl-windows-amd64.exe",
|
|
20
|
+
}
|
|
21
|
+
# PKL_VERSION = "0.26.3"
|
|
22
|
+
# BASE_PATH = "https://github.com/apple/pkl/releases/download/"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BinaryManager:
|
|
26
|
+
def __init__(self, download_binary=True):
|
|
27
|
+
binary_fp = self.is_command_available() or self._downloaded_binary_path()
|
|
28
|
+
need_to_download = binary_fp is None
|
|
29
|
+
if download_binary and need_to_download:
|
|
30
|
+
binary_dir = self._default_bin_dir()
|
|
31
|
+
binary_url: str = self._determine_binary_url()
|
|
32
|
+
binary_fp = self.download_binary(binary_dir, binary_url)
|
|
33
|
+
|
|
34
|
+
self.binary_path = binary_fp
|
|
35
|
+
|
|
36
|
+
# self.binary_dir: Path = binary_dir or self._default_bin_dir()
|
|
37
|
+
# self.binary_name: str = binary_name or self._determine_binary_name()
|
|
38
|
+
# self.binary_path: Path = self.binary_dir / self.binary_name
|
|
39
|
+
# self.binary_base_url = BASE_PATH + PKL_VERSION + "/"
|
|
40
|
+
# self.binary_url = self.binary_base_url + self.binary_path.name
|
|
41
|
+
|
|
42
|
+
def _downloaded_binary_path(self):
|
|
43
|
+
binary_dir: Path = self._default_bin_dir()
|
|
44
|
+
if not binary_dir.exists():
|
|
45
|
+
return None
|
|
46
|
+
ls = list(binary_dir.iterdir())
|
|
47
|
+
|
|
48
|
+
if len(ls) == 0:
|
|
49
|
+
return None
|
|
50
|
+
elif len(ls) == 1:
|
|
51
|
+
return Path(ls[0])
|
|
52
|
+
else:
|
|
53
|
+
raise Exception("Something went wrong")
|
|
54
|
+
|
|
55
|
+
@cache
|
|
56
|
+
def get_latest_pkl_release_urls(self):
|
|
57
|
+
url = "https://api.github.com/repos/apple/pkl/releases/latest"
|
|
58
|
+
response = requests.get(url)
|
|
59
|
+
release = response.json()
|
|
60
|
+
assets = release["assets"]
|
|
61
|
+
urls = {a["name"]: a["browser_download_url"] for a in assets}
|
|
62
|
+
return urls
|
|
63
|
+
|
|
64
|
+
def is_command_available(self):
|
|
65
|
+
"""
|
|
66
|
+
Check if a command is available in the system's PATH.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
if platform.system() == "Windows":
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
["where", "pkl"],
|
|
72
|
+
check=True,
|
|
73
|
+
stdout=subprocess.PIPE,
|
|
74
|
+
stderr=subprocess.PIPE,
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
result = subprocess.run(
|
|
78
|
+
["which", "pkl"],
|
|
79
|
+
check=True,
|
|
80
|
+
stdout=subprocess.PIPE,
|
|
81
|
+
stderr=subprocess.PIPE,
|
|
82
|
+
)
|
|
83
|
+
return result.stdout.decode().splitlines()[0].strip()
|
|
84
|
+
except subprocess.CalledProcessError:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def download_binary(self, binary_dir: Path, binary_url: str):
|
|
88
|
+
binary_dir.mkdir(exist_ok=True, parents=True)
|
|
89
|
+
return self._download_and_save_binary(binary_url, binary_dir)
|
|
90
|
+
|
|
91
|
+
def get_binary_filepath(self):
|
|
92
|
+
return str(self.binary_path)
|
|
93
|
+
|
|
94
|
+
def detect_system(self):
|
|
95
|
+
def is_alpine_linux():
|
|
96
|
+
return os.path.isfile("/etc/alpine-release")
|
|
97
|
+
|
|
98
|
+
def detect_processor_architecture():
|
|
99
|
+
architecture = platform.machine().lower()
|
|
100
|
+
if "arm" in architecture or "aarch64" in architecture:
|
|
101
|
+
return "ARM"
|
|
102
|
+
elif "x86_64" in architecture or "amd64" in architecture:
|
|
103
|
+
return "AMD64"
|
|
104
|
+
else:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
os_name = platform.system().lower()
|
|
108
|
+
if os_name == "linux" and is_alpine_linux():
|
|
109
|
+
os_name = "alpine-linux"
|
|
110
|
+
arch = detect_processor_architecture()
|
|
111
|
+
return os_name, arch
|
|
112
|
+
|
|
113
|
+
def _determine_binary_url(self) -> str:
|
|
114
|
+
os_name, arch = self.detect_system()
|
|
115
|
+
if arch == "ARM":
|
|
116
|
+
bin_name = ARM_BINARIES.get(os_name)
|
|
117
|
+
else:
|
|
118
|
+
bin_name = AMD64_BINARIES.get(os_name)
|
|
119
|
+
|
|
120
|
+
if bin_name is None:
|
|
121
|
+
raise OSError(
|
|
122
|
+
f"No compatible binary found for your system: {os_name}, {arch}"
|
|
123
|
+
)
|
|
124
|
+
name_to_url_map = self.get_latest_pkl_release_urls()
|
|
125
|
+
url = name_to_url_map[bin_name]
|
|
126
|
+
return url
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def _default_bin_dir(cls) -> Path:
|
|
130
|
+
bin_dir = Path(__file__).parent / "bin"
|
|
131
|
+
return bin_dir
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
def download_all_binary(self):
|
|
135
|
+
download_dir = self._default_bin_dir()
|
|
136
|
+
download_dir.mkdir(exist_ok=True)
|
|
137
|
+
for filename in BINARIES.values():
|
|
138
|
+
url = self.binary_base_url + filename
|
|
139
|
+
self._download_and_save_binary(url, download_dir)
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
@classmethod
|
|
143
|
+
def _fetch_binary(cls, url):
|
|
144
|
+
response = requests.get(url)
|
|
145
|
+
response.raise_for_status()
|
|
146
|
+
return response.content
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def _download_and_save_binary(
|
|
150
|
+
cls, url, target_dir: Path, filename=None, skip_exists=True
|
|
151
|
+
) -> Path:
|
|
152
|
+
assert target_dir.is_dir()
|
|
153
|
+
content = cls._fetch_binary(url)
|
|
154
|
+
|
|
155
|
+
default_filename = Path(url).name
|
|
156
|
+
binary_fp = target_dir / (filename or default_filename)
|
|
157
|
+
|
|
158
|
+
if skip_exists and binary_fp.exists():
|
|
159
|
+
return binary_fp
|
|
160
|
+
|
|
161
|
+
with open(binary_fp, "wb") as f:
|
|
162
|
+
f.write(content)
|
|
163
|
+
print(f"Successfully installed {binary_fp}")
|
|
164
|
+
os.chmod(binary_fp, os.stat(binary_fp).st_mode | stat.S_IXUSR)
|
|
165
|
+
return binary_fp
|
|
166
|
+
|
|
167
|
+
def check(self):
|
|
168
|
+
cmd = [self.binary_path, "-v"]
|
|
169
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
170
|
+
stdout = result.stdout
|
|
171
|
+
return stdout
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main():
|
|
175
|
+
parser = argparse.ArgumentParser()
|
|
176
|
+
parser.add_argument("--binary_dir")
|
|
177
|
+
args = parser.parse_args()
|
|
178
|
+
|
|
179
|
+
binary_dir = Path(args.binary_dir)
|
|
180
|
+
manager = BinaryManager(binary_dir=binary_dir)
|
|
181
|
+
manager.download_all_binary()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import atexit
|
|
2
2
|
import os
|
|
3
|
-
import platform
|
|
4
3
|
import signal
|
|
5
|
-
import stat
|
|
6
4
|
import subprocess
|
|
7
|
-
from pathlib import Path
|
|
8
5
|
|
|
9
6
|
import msgpack
|
|
10
|
-
import requests
|
|
11
|
-
|
|
12
|
-
BINARIES = {
|
|
13
|
-
("darwin", "64bit"): "pkl-macos-amd64",
|
|
14
|
-
("darwin", "aarch64"): "pkl-macos-aarch64",
|
|
15
|
-
("linux", "64bit"): "pkl-linux-amd64",
|
|
16
|
-
("linux", "aarch64"): "pkl-linux-aarch64",
|
|
17
|
-
("linux", "64bit", "alpine"): "pkl-alpine-linux-amd64",
|
|
18
|
-
}
|
|
19
|
-
PKL_VERSION = "0.25.3"
|
|
20
|
-
BASE_PATH = "https://github.com/apple/pkl/releases/download/"
|
|
21
7
|
|
|
22
8
|
|
|
23
9
|
def preexec_function():
|
|
@@ -25,53 +11,6 @@ def preexec_function():
|
|
|
25
11
|
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
26
12
|
|
|
27
13
|
|
|
28
|
-
def detect_system():
|
|
29
|
-
os_name = platform.system().lower()
|
|
30
|
-
arch, _ = platform.architecture()
|
|
31
|
-
return os_name, arch
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def is_alpine_linux():
|
|
35
|
-
return os.path.isfile("/etc/alpine-release")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def execute_binary(binary_path):
|
|
39
|
-
subprocess.run([binary_path, "server"], check=True)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def download_binary(file, target_fp):
|
|
43
|
-
url = BASE_PATH + PKL_VERSION + "/" + file
|
|
44
|
-
response = requests.get(url)
|
|
45
|
-
with open(target_fp, "wb") as f:
|
|
46
|
-
f.write(response.content)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def get_binary_path():
|
|
50
|
-
os_name, arch = detect_system()
|
|
51
|
-
if os_name == "linux" and is_alpine_linux():
|
|
52
|
-
os_name = "alpine"
|
|
53
|
-
binary_key = (os_name, arch)
|
|
54
|
-
if binary_key == ("linux", "64bit") and is_alpine_linux():
|
|
55
|
-
binary_key += ("alpine",)
|
|
56
|
-
bin_file = BINARIES.get(binary_key)
|
|
57
|
-
|
|
58
|
-
if bin_file is None:
|
|
59
|
-
raise OSError("No compatible binary found for your system.")
|
|
60
|
-
|
|
61
|
-
bin_parent_path = (Path("~/.pkl/bin/") / PKL_VERSION).expanduser()
|
|
62
|
-
binary_path = bin_parent_path / bin_file
|
|
63
|
-
|
|
64
|
-
if not binary_path.exists():
|
|
65
|
-
binary_path.parent.mkdir(exist_ok=True, parents=True)
|
|
66
|
-
download_binary(bin_file, binary_path)
|
|
67
|
-
|
|
68
|
-
current_permissions = os.stat(binary_path).st_mode
|
|
69
|
-
new_permissions = current_permissions | stat.S_IXUSR
|
|
70
|
-
os.chmod(binary_path, new_permissions)
|
|
71
|
-
|
|
72
|
-
return binary_path
|
|
73
|
-
|
|
74
|
-
|
|
75
14
|
_PROCESSES = []
|
|
76
15
|
|
|
77
16
|
|
|
@@ -86,7 +25,12 @@ atexit.register(terminate_processes)
|
|
|
86
25
|
|
|
87
26
|
class PKLServer:
|
|
88
27
|
def __init__(self, cmd=None, debug=False):
|
|
89
|
-
|
|
28
|
+
from pkl.binary_manager import BinaryManager
|
|
29
|
+
|
|
30
|
+
manager = BinaryManager()
|
|
31
|
+
binary_path = manager.get_binary_filepath()
|
|
32
|
+
|
|
33
|
+
self.cmd = cmd or [binary_path, "server"]
|
|
90
34
|
self.next_request_id = 1
|
|
91
35
|
self.unpacker = msgpack.Unpacker()
|
|
92
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pkl-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Python library for Apple's PKL.
|
|
5
5
|
Author-email: Jungwoo Yang <jwyang0213@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -31,7 +31,7 @@ Project-URL: Source, https://github.com/jw-y/pkl-python
|
|
|
31
31
|
Classifier: Programming Language :: Python :: 3
|
|
32
32
|
Classifier: License :: OSI Approved :: MIT License
|
|
33
33
|
Classifier: Operating System :: OS Independent
|
|
34
|
-
Requires-Python: >=3.
|
|
34
|
+
Requires-Python: >=3.8
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
License-File: LICENSE
|
|
37
37
|
Requires-Dist: msgpack>=1.0.8
|
|
@@ -68,6 +68,7 @@ Here's how you can start using `pkl-python` to load a PKL module:
|
|
|
68
68
|
import pkl
|
|
69
69
|
|
|
70
70
|
config = pkl.load("path/to/pkl/example_module.pkl")
|
|
71
|
+
config = pkl.loads("a: Int = 1 + 1")
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
### Code Generation
|
|
@@ -78,10 +79,6 @@ However, codegen lets you expect contents of pkl files within Python modules.
|
|
|
78
79
|
pkl-gen-python path/to/pkl/example_module.pkl
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
### Status
|
|
82
|
-
* Evaluator API: functional
|
|
83
|
-
* Code Generation: functional, but needs refining
|
|
84
|
-
|
|
85
82
|
### TODO
|
|
86
83
|
* [x] (codgen) pip binary installation
|
|
87
84
|
* [ ] (codgen) gatherer depth-first ordering
|
|
@@ -89,7 +86,6 @@ pkl-gen-python path/to/pkl/example_module.pkl
|
|
|
89
86
|
|
|
90
87
|
|
|
91
88
|
## Advanced Features
|
|
92
|
-
For details on the parameters, refer [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
93
89
|
|
|
94
90
|
```python
|
|
95
91
|
import pkl
|
|
@@ -100,6 +96,11 @@ config = pkl.load(None, module_text="a: Int = 1 + 1")
|
|
|
100
96
|
config = pkl.load("./tests/types.pkl", debug=True)
|
|
101
97
|
```
|
|
102
98
|
|
|
99
|
+
### `pkl.load` Parameters Detail
|
|
100
|
+
For details on the parameters, refer
|
|
101
|
+
* [`pkl eval`](https://pkl-lang.org/main/current/pkl-cli/index.html#command-eval)
|
|
102
|
+
* [Message Passing API](https://pkl-lang.org/main/current/bindings-specification/message-passing-api.html).
|
|
103
|
+
|
|
103
104
|
### Custom Readers
|
|
104
105
|
It is possible to add module or resource or module readers:
|
|
105
106
|
```python
|
|
@@ -123,7 +124,7 @@ opts = PreconfiguredOptions(
|
|
|
123
124
|
moduleReaders=[TestModuleReader("customfs", True, True, True)]
|
|
124
125
|
)
|
|
125
126
|
opts.allowedModules.append("customfs:")
|
|
126
|
-
config = pkl.load("./tests/myModule.pkl", evaluator_options=opts)
|
|
127
|
+
config = pkl.load("./tests/pkls/myModule.pkl", evaluator_options=opts)
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
## Appendix
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
-
setup.py
|
|
5
4
|
src/pkl_gen_python.py
|
|
6
5
|
src/pkl/VERSION
|
|
7
6
|
src/pkl/__init__.py
|
|
7
|
+
src/pkl/binary_manager.py
|
|
8
8
|
src/pkl/evaluator_manager.py
|
|
9
9
|
src/pkl/evaluator_options.py
|
|
10
10
|
src/pkl/msgapi.py
|
|
@@ -18,6 +18,7 @@ src/pkl_python.egg-info/dependency_links.txt
|
|
|
18
18
|
src/pkl_python.egg-info/entry_points.txt
|
|
19
19
|
src/pkl_python.egg-info/requires.txt
|
|
20
20
|
src/pkl_python.egg-info/top_level.txt
|
|
21
|
+
tests/test_binary.py
|
|
21
22
|
tests/test_evaluator_manager.py
|
|
22
23
|
tests/test_fixtures.py
|
|
23
24
|
tests/test_load.py
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
import pkl
|
|
7
|
+
from pkl.binary_manager import BinaryManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def remove_pkl_from_path(monkeypatch):
|
|
11
|
+
manager = BinaryManager()
|
|
12
|
+
path = manager.is_command_available()
|
|
13
|
+
if path is None:
|
|
14
|
+
return
|
|
15
|
+
path = path.rsplit("/", 1)[0]
|
|
16
|
+
original_path = os.environ.get("PATH", "")
|
|
17
|
+
paths = original_path.split(os.pathsep)
|
|
18
|
+
paths = [p for p in paths if path not in p]
|
|
19
|
+
monkeypatch.setenv("PATH", os.pathsep.join(paths))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def remove_pkl_binary():
|
|
23
|
+
path = Path(pkl.__file__)
|
|
24
|
+
ls = list((path.parent / "bin").iterdir())
|
|
25
|
+
if len(ls) == 0:
|
|
26
|
+
return
|
|
27
|
+
assert len(ls) == 1
|
|
28
|
+
binary_path = ls[0]
|
|
29
|
+
os.remove(binary_path)
|
|
30
|
+
return binary_path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_latest_release():
|
|
34
|
+
manager = BinaryManager()
|
|
35
|
+
urls = manager.get_latest_pkl_release_urls()
|
|
36
|
+
assert len(urls) == 7
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_without_download_binary():
|
|
40
|
+
manager = BinaryManager()
|
|
41
|
+
output = manager.check()
|
|
42
|
+
assert output.lower().startswith("pkl")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_download_binary(monkeypatch: pytest.MonkeyPatch):
|
|
46
|
+
remove_pkl_binary()
|
|
47
|
+
remove_pkl_from_path(monkeypatch)
|
|
48
|
+
manager = BinaryManager()
|
|
49
|
+
output = manager.check()
|
|
50
|
+
assert output.lower().startswith("pkl")
|
|
51
|
+
|
|
52
|
+
removed = remove_pkl_binary()
|
|
53
|
+
assert manager.binary_path == removed
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_binary_file_exists():
|
|
57
|
+
manager = BinaryManager()
|
|
58
|
+
manager.is_command_available()
|
|
59
|
+
|
|
60
|
+
fp = manager.get_binary_filepath()
|
|
61
|
+
assert os.path.exists(fp), f"Binary file not found at {fp}"
|
|
@@ -5,6 +5,11 @@ def test_load():
|
|
|
5
5
|
_ = pkl.load("./tests/pkls/types.pkl")
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
def test_loads():
|
|
9
|
+
config = pkl.loads("a: Int = 1 + 1")
|
|
10
|
+
assert config.a == 2
|
|
11
|
+
|
|
12
|
+
|
|
8
13
|
def test_load_expr():
|
|
9
14
|
config = pkl.load("./tests/pkls/types.pkl", expr="datasize")
|
|
10
15
|
assert config.__class__.__name__ == "DataSize"
|
pkl-python-0.1.14/setup.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import platform
|
|
3
|
-
import stat
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import setuptools
|
|
7
|
-
from setuptools.command.install import install
|
|
8
|
-
|
|
9
|
-
BINARIES = {
|
|
10
|
-
("darwin", "64bit"): "pkl-macos-amd64",
|
|
11
|
-
("darwin", "aarch64"): "pkl-macos-aarch64",
|
|
12
|
-
("linux", "64bit"): "pkl-linux-amd64",
|
|
13
|
-
("linux", "aarch64"): "pkl-linux-aarch64",
|
|
14
|
-
("linux", "64bit", "alpine"): "pkl-alpine-linux-amd64",
|
|
15
|
-
}
|
|
16
|
-
PKL_VERSION = "0.25.3"
|
|
17
|
-
BASE_PATH = "https://github.com/apple/pkl/releases/download/"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def detect_system():
|
|
21
|
-
os_name = platform.system().lower()
|
|
22
|
-
arch, _ = platform.architecture()
|
|
23
|
-
return os_name, arch
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def is_alpine_linux():
|
|
27
|
-
return os.path.isfile("/etc/alpine-release")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def download_binary(file, target_fp):
|
|
31
|
-
import requests
|
|
32
|
-
|
|
33
|
-
url = BASE_PATH + PKL_VERSION + "/" + file
|
|
34
|
-
response = requests.get(url)
|
|
35
|
-
with open(target_fp, "wb") as f:
|
|
36
|
-
f.write(response.content)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get_binary_path():
|
|
40
|
-
os_name, arch = detect_system()
|
|
41
|
-
if os_name == "linux" and is_alpine_linux():
|
|
42
|
-
os_name = "alpine"
|
|
43
|
-
binary_key = (os_name, arch)
|
|
44
|
-
if binary_key == ("linux", "64bit") and is_alpine_linux():
|
|
45
|
-
binary_key += ("alpine",)
|
|
46
|
-
bin_file = BINARIES.get(binary_key)
|
|
47
|
-
|
|
48
|
-
if bin_file is None:
|
|
49
|
-
raise OSError("No compatible binary found for your system.")
|
|
50
|
-
|
|
51
|
-
bin_parent_path = (Path("~/.pkl/bin/") / PKL_VERSION).expanduser()
|
|
52
|
-
binary_path = bin_parent_path / bin_file
|
|
53
|
-
|
|
54
|
-
if not binary_path.exists():
|
|
55
|
-
binary_path.parent.mkdir(exist_ok=True, parents=True)
|
|
56
|
-
download_binary(bin_file, binary_path)
|
|
57
|
-
|
|
58
|
-
current_permissions = os.stat(binary_path).st_mode
|
|
59
|
-
new_permissions = current_permissions | stat.S_IXUSR
|
|
60
|
-
os.chmod(binary_path, new_permissions)
|
|
61
|
-
|
|
62
|
-
return binary_path
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class CustomInstallCommand(install):
|
|
66
|
-
def run(self):
|
|
67
|
-
get_binary_path()
|
|
68
|
-
install.run(self)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
setuptools.setup(
|
|
72
|
-
cmdclass={"install": CustomInstallCommand},
|
|
73
|
-
setup_requires=["requests"],
|
|
74
|
-
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.1.14
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|