cppkh-interface 0.1.3__tar.gz → 0.2.0__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.
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/PKG-INFO +1 -1
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/README.md +68 -68
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/build_backend/cppkh_interface_build_backend.py +207 -207
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/cppkh_interface/__init__.py +29 -25
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/cppkh_interface/__main__.py +4 -4
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/cppkh_interface/main.py +632 -544
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/cppkh_interface/py.typed +1 -1
- {cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/pyproject.toml +30 -30
- cppkh_interface-0.1.3/cppkh_interface/data/src/main.cpp +0 -3657
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
# cppkh-interface
|
|
2
|
-
|
|
3
|
-
`cppkh-interface` is a Python package for computing integer Khovanov homology
|
|
4
|
-
with the C++ `cppkh` implementation.
|
|
5
|
-
|
|
6
|
-
Version `0.1.3` has no runtime Python-package dependencies. Link crossing signs,
|
|
7
|
-
PD validation, R1 removal, and nugatory-crossing removal all use the bundled
|
|
8
|
-
canonical `cppkh` C++ source and its SageMath-compatible orientation rules.
|
|
9
|
-
|
|
10
|
-
The package is compatible with the main `javakh-interface` function:
|
|
11
|
-
|
|
12
|
-
```python
|
|
13
|
-
import cppkh_interface
|
|
14
|
-
|
|
15
|
-
pd_code = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
16
|
-
print(cppkh_interface.solve_khovanov(pd_code, de_r1=True, de_k8=True))
|
|
17
|
-
print(cppkh_interface.solve_many_khovanov([pd_code, pd_code]))
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Unlike wrappers that ship a prebuilt DLL or shared object, this package ships
|
|
21
|
-
the `cppkh` C++ source file in built distributions and compiles a local
|
|
22
|
-
executable on first use using only Python's standard library. The compiled
|
|
23
|
-
executable is cached for later calls.
|
|
24
|
-
|
|
25
|
-
In the repository checkout, the package does not keep a committed backup copy
|
|
26
|
-
of the C++ source. The build backend copies `../../src/main.cpp` into the
|
|
27
|
-
package data directory only while the PEP 517 build is running, then removes
|
|
28
|
-
that temporary copy.
|
|
29
|
-
|
|
30
|
-
## Install
|
|
31
|
-
|
|
32
|
-
```sh
|
|
33
|
-
pip install cppkh-interface
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
A C++14 compiler must be available at runtime. The package looks at
|
|
37
|
-
`CPPKH_INTERFACE_CXX`, then `CXX`, then searches `PATH` for `g++`, `clang++`, or
|
|
38
|
-
`c++`. To select a compiler explicitly:
|
|
39
|
-
|
|
40
|
-
```sh
|
|
41
|
-
CPPKH_INTERFACE_CXX=clang++ python your_script.py
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Windows PowerShell:
|
|
45
|
-
|
|
46
|
-
```powershell
|
|
47
|
-
$env:CPPKH_INTERFACE_CXX = "C:\path\to\g++.exe"
|
|
48
|
-
python your_script.py
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Build And Publish
|
|
52
|
-
|
|
53
|
-
From this directory:
|
|
54
|
-
|
|
55
|
-
```sh
|
|
56
|
-
python -m build
|
|
57
|
-
poetry publish
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Do not use `poetry build` or `poetry publish --build`: Poetry's direct builder
|
|
61
|
-
bypasses the source-synchronizing PEP 517 backend. Build first with
|
|
62
|
-
`python -m build`, inspect/test the wheel, then publish the existing artifacts.
|
|
63
|
-
|
|
64
|
-
For local testing:
|
|
65
|
-
|
|
66
|
-
```sh
|
|
67
|
-
poetry run python -m cppkh_interface "[[1,5,2,4], [3,1,4,6], [5,3,6,2]]"
|
|
68
|
-
```
|
|
1
|
+
# cppkh-interface
|
|
2
|
+
|
|
3
|
+
`cppkh-interface` is a Python package for computing integer Khovanov homology
|
|
4
|
+
with the C++ `cppkh` implementation.
|
|
5
|
+
|
|
6
|
+
Version `0.1.3` has no runtime Python-package dependencies. Link crossing signs,
|
|
7
|
+
PD validation, R1 removal, and nugatory-crossing removal all use the bundled
|
|
8
|
+
canonical `cppkh` C++ source and its SageMath-compatible orientation rules.
|
|
9
|
+
|
|
10
|
+
The package is compatible with the main `javakh-interface` function:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
import cppkh_interface
|
|
14
|
+
|
|
15
|
+
pd_code = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
16
|
+
print(cppkh_interface.solve_khovanov(pd_code, de_r1=True, de_k8=True))
|
|
17
|
+
print(cppkh_interface.solve_many_khovanov([pd_code, pd_code]))
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Unlike wrappers that ship a prebuilt DLL or shared object, this package ships
|
|
21
|
+
the `cppkh` C++ source file in built distributions and compiles a local
|
|
22
|
+
executable on first use using only Python's standard library. The compiled
|
|
23
|
+
executable is cached for later calls.
|
|
24
|
+
|
|
25
|
+
In the repository checkout, the package does not keep a committed backup copy
|
|
26
|
+
of the C++ source. The build backend copies `../../src/main.cpp` into the
|
|
27
|
+
package data directory only while the PEP 517 build is running, then removes
|
|
28
|
+
that temporary copy.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install cppkh-interface
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A C++14 compiler must be available at runtime. The package looks at
|
|
37
|
+
`CPPKH_INTERFACE_CXX`, then `CXX`, then searches `PATH` for `g++`, `clang++`, or
|
|
38
|
+
`c++`. To select a compiler explicitly:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
CPPKH_INTERFACE_CXX=clang++ python your_script.py
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Windows PowerShell:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
$env:CPPKH_INTERFACE_CXX = "C:\path\to\g++.exe"
|
|
48
|
+
python your_script.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Build And Publish
|
|
52
|
+
|
|
53
|
+
From this directory:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
python -m build
|
|
57
|
+
poetry publish
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Do not use `poetry build` or `poetry publish --build`: Poetry's direct builder
|
|
61
|
+
bypasses the source-synchronizing PEP 517 backend. Build first with
|
|
62
|
+
`python -m build`, inspect/test the wheel, then publish the existing artifacts.
|
|
63
|
+
|
|
64
|
+
For local testing:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
poetry run python -m cppkh_interface "[[1,5,2,4], [3,1,4,6], [5,3,6,2]]"
|
|
68
|
+
```
|
{cppkh_interface-0.1.3 → cppkh_interface-0.2.0}/build_backend/cppkh_interface_build_backend.py
RENAMED
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import base64
|
|
4
|
-
import hashlib
|
|
5
|
-
import io
|
|
6
|
-
import shutil
|
|
7
|
-
import tarfile
|
|
8
|
-
import tempfile
|
|
9
|
-
import zipfile
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from poetry.core.masonry import api as poetry_api
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
-
REPO_ROOT = PROJECT_ROOT.parents[1]
|
|
17
|
-
SOURCE = REPO_ROOT / "src" / "main.cpp"
|
|
18
|
-
TARGET = PROJECT_ROOT / "cppkh_interface" / "data" / "src" / "main.cpp"
|
|
19
|
-
WHEEL_SOURCE_NAME = "cppkh_interface/data/src/main.cpp"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _sync_cpp_source() -> None:
|
|
23
|
-
if SOURCE.exists():
|
|
24
|
-
TARGET.parent.mkdir(parents=True, exist_ok=True)
|
|
25
|
-
shutil.copyfile(SOURCE, TARGET)
|
|
26
|
-
return
|
|
27
|
-
if TARGET.exists():
|
|
28
|
-
return
|
|
29
|
-
raise FileNotFoundError(
|
|
30
|
-
"cppkh source file was not found. Expected either "
|
|
31
|
-
f"{SOURCE} in the repository checkout or {TARGET} in the source tree."
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _clean_cpp_source() -> None:
|
|
36
|
-
if TARGET.exists() and SOURCE.exists():
|
|
37
|
-
TARGET.unlink()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _source_bytes() -> bytes:
|
|
41
|
-
if TARGET.exists():
|
|
42
|
-
return TARGET.read_bytes()
|
|
43
|
-
if SOURCE.exists():
|
|
44
|
-
return SOURCE.read_bytes()
|
|
45
|
-
raise FileNotFoundError("cppkh source file was not available for packaging")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _wheel_hash_and_size(data: bytes) -> tuple[str, str]:
|
|
49
|
-
digest = hashlib.sha256(data).digest()
|
|
50
|
-
encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
|
|
51
|
-
return f"sha256={encoded}", str(len(data))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _make_universal_wheel_metadata(data: bytes) -> bytes:
|
|
55
|
-
text = data.decode("utf-8")
|
|
56
|
-
lines = []
|
|
57
|
-
tag_written = False
|
|
58
|
-
root_written = False
|
|
59
|
-
for line in text.splitlines():
|
|
60
|
-
if line.startswith("Root-Is-Purelib:"):
|
|
61
|
-
lines.append("Root-Is-Purelib: true")
|
|
62
|
-
root_written = True
|
|
63
|
-
elif line.startswith("Tag:"):
|
|
64
|
-
if not tag_written:
|
|
65
|
-
lines.append("Tag: py3-none-any")
|
|
66
|
-
tag_written = True
|
|
67
|
-
else:
|
|
68
|
-
lines.append(line)
|
|
69
|
-
if not root_written:
|
|
70
|
-
lines.append("Root-Is-Purelib: true")
|
|
71
|
-
if not tag_written:
|
|
72
|
-
lines.append("Tag: py3-none-any")
|
|
73
|
-
return ("\n".join(lines) + "\n").encode("utf-8")
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _universal_wheel_path(wheel_path: Path, record_name: str) -> Path:
|
|
77
|
-
dist_info = record_name.split("/", 1)[0]
|
|
78
|
-
base = dist_info.removesuffix(".dist-info")
|
|
79
|
-
return wheel_path.with_name(f"{base}-py3-none-any.whl")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _rewrite_wheel_with_source(wheel_path: Path) -> Path:
|
|
83
|
-
source_data = _source_bytes()
|
|
84
|
-
rows: list[tuple[str, str, str]] = []
|
|
85
|
-
|
|
86
|
-
with zipfile.ZipFile(wheel_path, "r") as zin:
|
|
87
|
-
record_name = next(
|
|
88
|
-
(name for name in zin.namelist() if name.endswith(".dist-info/RECORD")),
|
|
89
|
-
None,
|
|
90
|
-
)
|
|
91
|
-
if record_name is None:
|
|
92
|
-
raise RuntimeError(f"wheel RECORD file not found in {wheel_path}")
|
|
93
|
-
output_path = _universal_wheel_path(wheel_path, record_name)
|
|
94
|
-
temp_path = output_path.with_name(output_path.name + ".tmp")
|
|
95
|
-
|
|
96
|
-
with zipfile.ZipFile(temp_path, "w", compression=zipfile.ZIP_DEFLATED) as zout:
|
|
97
|
-
wheel_metadata_name = record_name.removesuffix("RECORD") + "WHEEL"
|
|
98
|
-
|
|
99
|
-
for item in zin.infolist():
|
|
100
|
-
if item.filename in (WHEEL_SOURCE_NAME, record_name):
|
|
101
|
-
continue
|
|
102
|
-
data = zin.read(item.filename)
|
|
103
|
-
if item.filename == wheel_metadata_name:
|
|
104
|
-
data = _make_universal_wheel_metadata(data)
|
|
105
|
-
zout.writestr(item, data)
|
|
106
|
-
if not item.is_dir():
|
|
107
|
-
digest, size = _wheel_hash_and_size(data)
|
|
108
|
-
rows.append((item.filename, digest, size))
|
|
109
|
-
|
|
110
|
-
source_info = zipfile.ZipInfo(WHEEL_SOURCE_NAME, date_time=(2016, 1, 1, 0, 0, 0))
|
|
111
|
-
source_info.compress_type = zipfile.ZIP_DEFLATED
|
|
112
|
-
zout.writestr(source_info, source_data)
|
|
113
|
-
digest, size = _wheel_hash_and_size(source_data)
|
|
114
|
-
rows.append((WHEEL_SOURCE_NAME, digest, size))
|
|
115
|
-
|
|
116
|
-
rows.append((record_name, "", ""))
|
|
117
|
-
record_text = "".join(f"{path},{digest},{size}\n" for path, digest, size in rows)
|
|
118
|
-
record_info = zipfile.ZipInfo(record_name, date_time=(2016, 1, 1, 0, 0, 0))
|
|
119
|
-
record_info.compress_type = zipfile.ZIP_DEFLATED
|
|
120
|
-
zout.writestr(record_info, record_text.encode("utf-8"))
|
|
121
|
-
|
|
122
|
-
temp_path.replace(output_path)
|
|
123
|
-
if output_path != wheel_path and wheel_path.exists():
|
|
124
|
-
wheel_path.unlink()
|
|
125
|
-
return output_path
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def finalize_poetry_wheels() -> None:
|
|
129
|
-
dist = PROJECT_ROOT / "dist"
|
|
130
|
-
if not dist.exists():
|
|
131
|
-
return
|
|
132
|
-
for wheel_path in sorted(dist.glob("cppkh_interface-*.whl")):
|
|
133
|
-
_rewrite_wheel_with_source(wheel_path)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _rewrite_sdist_with_source(sdist_path: Path) -> None:
|
|
137
|
-
source_data = _source_bytes()
|
|
138
|
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz")
|
|
139
|
-
temp_path = Path(temp_file.name)
|
|
140
|
-
temp_file.close()
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
with tarfile.open(sdist_path, "r:gz") as tin, tarfile.open(temp_path, "w:gz") as tout:
|
|
144
|
-
names = tin.getnames()
|
|
145
|
-
if not names:
|
|
146
|
-
raise RuntimeError(f"sdist is empty: {sdist_path}")
|
|
147
|
-
root = names[0].split("/", 1)[0]
|
|
148
|
-
source_name = f"{root}/{WHEEL_SOURCE_NAME}"
|
|
149
|
-
|
|
150
|
-
for member in tin.getmembers():
|
|
151
|
-
if member.name == source_name:
|
|
152
|
-
continue
|
|
153
|
-
if member.isfile():
|
|
154
|
-
extracted = tin.extractfile(member)
|
|
155
|
-
if extracted is None:
|
|
156
|
-
raise RuntimeError(f"could not read {member.name} from {sdist_path}")
|
|
157
|
-
with extracted:
|
|
158
|
-
tout.addfile(member, extracted)
|
|
159
|
-
else:
|
|
160
|
-
tout.addfile(member)
|
|
161
|
-
|
|
162
|
-
source_info = tarfile.TarInfo(source_name)
|
|
163
|
-
source_info.size = len(source_data)
|
|
164
|
-
source_info.mtime = 0
|
|
165
|
-
source_info.mode = 0o644
|
|
166
|
-
tout.addfile(source_info, io.BytesIO(source_data))
|
|
167
|
-
|
|
168
|
-
temp_path.replace(sdist_path)
|
|
169
|
-
finally:
|
|
170
|
-
if temp_path.exists():
|
|
171
|
-
temp_path.unlink()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def get_requires_for_build_wheel(config_settings=None):
|
|
175
|
-
return poetry_api.get_requires_for_build_wheel(config_settings)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def get_requires_for_build_sdist(config_settings=None):
|
|
179
|
-
return poetry_api.get_requires_for_build_sdist(config_settings)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
|
|
183
|
-
_sync_cpp_source()
|
|
184
|
-
try:
|
|
185
|
-
return poetry_api.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
|
|
186
|
-
finally:
|
|
187
|
-
_clean_cpp_source()
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
|
|
191
|
-
_sync_cpp_source()
|
|
192
|
-
try:
|
|
193
|
-
wheel_name = poetry_api.build_wheel(wheel_directory, config_settings, metadata_directory)
|
|
194
|
-
final_path = _rewrite_wheel_with_source(Path(wheel_directory) / wheel_name)
|
|
195
|
-
return final_path.name
|
|
196
|
-
finally:
|
|
197
|
-
_clean_cpp_source()
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def build_sdist(sdist_directory, config_settings=None):
|
|
201
|
-
_sync_cpp_source()
|
|
202
|
-
try:
|
|
203
|
-
sdist_name = poetry_api.build_sdist(sdist_directory, config_settings)
|
|
204
|
-
_rewrite_sdist_with_source(Path(sdist_directory) / sdist_name)
|
|
205
|
-
return sdist_name
|
|
206
|
-
finally:
|
|
207
|
-
_clean_cpp_source()
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import hashlib
|
|
5
|
+
import io
|
|
6
|
+
import shutil
|
|
7
|
+
import tarfile
|
|
8
|
+
import tempfile
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from poetry.core.masonry import api as poetry_api
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
REPO_ROOT = PROJECT_ROOT.parents[1]
|
|
17
|
+
SOURCE = REPO_ROOT / "src" / "main.cpp"
|
|
18
|
+
TARGET = PROJECT_ROOT / "cppkh_interface" / "data" / "src" / "main.cpp"
|
|
19
|
+
WHEEL_SOURCE_NAME = "cppkh_interface/data/src/main.cpp"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _sync_cpp_source() -> None:
|
|
23
|
+
if SOURCE.exists():
|
|
24
|
+
TARGET.parent.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
shutil.copyfile(SOURCE, TARGET)
|
|
26
|
+
return
|
|
27
|
+
if TARGET.exists():
|
|
28
|
+
return
|
|
29
|
+
raise FileNotFoundError(
|
|
30
|
+
"cppkh source file was not found. Expected either "
|
|
31
|
+
f"{SOURCE} in the repository checkout or {TARGET} in the source tree."
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _clean_cpp_source() -> None:
|
|
36
|
+
if TARGET.exists() and SOURCE.exists():
|
|
37
|
+
TARGET.unlink()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _source_bytes() -> bytes:
|
|
41
|
+
if TARGET.exists():
|
|
42
|
+
return TARGET.read_bytes()
|
|
43
|
+
if SOURCE.exists():
|
|
44
|
+
return SOURCE.read_bytes()
|
|
45
|
+
raise FileNotFoundError("cppkh source file was not available for packaging")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _wheel_hash_and_size(data: bytes) -> tuple[str, str]:
|
|
49
|
+
digest = hashlib.sha256(data).digest()
|
|
50
|
+
encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
|
|
51
|
+
return f"sha256={encoded}", str(len(data))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _make_universal_wheel_metadata(data: bytes) -> bytes:
|
|
55
|
+
text = data.decode("utf-8")
|
|
56
|
+
lines = []
|
|
57
|
+
tag_written = False
|
|
58
|
+
root_written = False
|
|
59
|
+
for line in text.splitlines():
|
|
60
|
+
if line.startswith("Root-Is-Purelib:"):
|
|
61
|
+
lines.append("Root-Is-Purelib: true")
|
|
62
|
+
root_written = True
|
|
63
|
+
elif line.startswith("Tag:"):
|
|
64
|
+
if not tag_written:
|
|
65
|
+
lines.append("Tag: py3-none-any")
|
|
66
|
+
tag_written = True
|
|
67
|
+
else:
|
|
68
|
+
lines.append(line)
|
|
69
|
+
if not root_written:
|
|
70
|
+
lines.append("Root-Is-Purelib: true")
|
|
71
|
+
if not tag_written:
|
|
72
|
+
lines.append("Tag: py3-none-any")
|
|
73
|
+
return ("\n".join(lines) + "\n").encode("utf-8")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _universal_wheel_path(wheel_path: Path, record_name: str) -> Path:
|
|
77
|
+
dist_info = record_name.split("/", 1)[0]
|
|
78
|
+
base = dist_info.removesuffix(".dist-info")
|
|
79
|
+
return wheel_path.with_name(f"{base}-py3-none-any.whl")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _rewrite_wheel_with_source(wheel_path: Path) -> Path:
|
|
83
|
+
source_data = _source_bytes()
|
|
84
|
+
rows: list[tuple[str, str, str]] = []
|
|
85
|
+
|
|
86
|
+
with zipfile.ZipFile(wheel_path, "r") as zin:
|
|
87
|
+
record_name = next(
|
|
88
|
+
(name for name in zin.namelist() if name.endswith(".dist-info/RECORD")),
|
|
89
|
+
None,
|
|
90
|
+
)
|
|
91
|
+
if record_name is None:
|
|
92
|
+
raise RuntimeError(f"wheel RECORD file not found in {wheel_path}")
|
|
93
|
+
output_path = _universal_wheel_path(wheel_path, record_name)
|
|
94
|
+
temp_path = output_path.with_name(output_path.name + ".tmp")
|
|
95
|
+
|
|
96
|
+
with zipfile.ZipFile(temp_path, "w", compression=zipfile.ZIP_DEFLATED) as zout:
|
|
97
|
+
wheel_metadata_name = record_name.removesuffix("RECORD") + "WHEEL"
|
|
98
|
+
|
|
99
|
+
for item in zin.infolist():
|
|
100
|
+
if item.filename in (WHEEL_SOURCE_NAME, record_name):
|
|
101
|
+
continue
|
|
102
|
+
data = zin.read(item.filename)
|
|
103
|
+
if item.filename == wheel_metadata_name:
|
|
104
|
+
data = _make_universal_wheel_metadata(data)
|
|
105
|
+
zout.writestr(item, data)
|
|
106
|
+
if not item.is_dir():
|
|
107
|
+
digest, size = _wheel_hash_and_size(data)
|
|
108
|
+
rows.append((item.filename, digest, size))
|
|
109
|
+
|
|
110
|
+
source_info = zipfile.ZipInfo(WHEEL_SOURCE_NAME, date_time=(2016, 1, 1, 0, 0, 0))
|
|
111
|
+
source_info.compress_type = zipfile.ZIP_DEFLATED
|
|
112
|
+
zout.writestr(source_info, source_data)
|
|
113
|
+
digest, size = _wheel_hash_and_size(source_data)
|
|
114
|
+
rows.append((WHEEL_SOURCE_NAME, digest, size))
|
|
115
|
+
|
|
116
|
+
rows.append((record_name, "", ""))
|
|
117
|
+
record_text = "".join(f"{path},{digest},{size}\n" for path, digest, size in rows)
|
|
118
|
+
record_info = zipfile.ZipInfo(record_name, date_time=(2016, 1, 1, 0, 0, 0))
|
|
119
|
+
record_info.compress_type = zipfile.ZIP_DEFLATED
|
|
120
|
+
zout.writestr(record_info, record_text.encode("utf-8"))
|
|
121
|
+
|
|
122
|
+
temp_path.replace(output_path)
|
|
123
|
+
if output_path != wheel_path and wheel_path.exists():
|
|
124
|
+
wheel_path.unlink()
|
|
125
|
+
return output_path
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def finalize_poetry_wheels() -> None:
|
|
129
|
+
dist = PROJECT_ROOT / "dist"
|
|
130
|
+
if not dist.exists():
|
|
131
|
+
return
|
|
132
|
+
for wheel_path in sorted(dist.glob("cppkh_interface-*.whl")):
|
|
133
|
+
_rewrite_wheel_with_source(wheel_path)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _rewrite_sdist_with_source(sdist_path: Path) -> None:
|
|
137
|
+
source_data = _source_bytes()
|
|
138
|
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz")
|
|
139
|
+
temp_path = Path(temp_file.name)
|
|
140
|
+
temp_file.close()
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
with tarfile.open(sdist_path, "r:gz") as tin, tarfile.open(temp_path, "w:gz") as tout:
|
|
144
|
+
names = tin.getnames()
|
|
145
|
+
if not names:
|
|
146
|
+
raise RuntimeError(f"sdist is empty: {sdist_path}")
|
|
147
|
+
root = names[0].split("/", 1)[0]
|
|
148
|
+
source_name = f"{root}/{WHEEL_SOURCE_NAME}"
|
|
149
|
+
|
|
150
|
+
for member in tin.getmembers():
|
|
151
|
+
if member.name == source_name:
|
|
152
|
+
continue
|
|
153
|
+
if member.isfile():
|
|
154
|
+
extracted = tin.extractfile(member)
|
|
155
|
+
if extracted is None:
|
|
156
|
+
raise RuntimeError(f"could not read {member.name} from {sdist_path}")
|
|
157
|
+
with extracted:
|
|
158
|
+
tout.addfile(member, extracted)
|
|
159
|
+
else:
|
|
160
|
+
tout.addfile(member)
|
|
161
|
+
|
|
162
|
+
source_info = tarfile.TarInfo(source_name)
|
|
163
|
+
source_info.size = len(source_data)
|
|
164
|
+
source_info.mtime = 0
|
|
165
|
+
source_info.mode = 0o644
|
|
166
|
+
tout.addfile(source_info, io.BytesIO(source_data))
|
|
167
|
+
|
|
168
|
+
temp_path.replace(sdist_path)
|
|
169
|
+
finally:
|
|
170
|
+
if temp_path.exists():
|
|
171
|
+
temp_path.unlink()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def get_requires_for_build_wheel(config_settings=None):
|
|
175
|
+
return poetry_api.get_requires_for_build_wheel(config_settings)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_requires_for_build_sdist(config_settings=None):
|
|
179
|
+
return poetry_api.get_requires_for_build_sdist(config_settings)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
|
|
183
|
+
_sync_cpp_source()
|
|
184
|
+
try:
|
|
185
|
+
return poetry_api.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
|
|
186
|
+
finally:
|
|
187
|
+
_clean_cpp_source()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
|
|
191
|
+
_sync_cpp_source()
|
|
192
|
+
try:
|
|
193
|
+
wheel_name = poetry_api.build_wheel(wheel_directory, config_settings, metadata_directory)
|
|
194
|
+
final_path = _rewrite_wheel_with_source(Path(wheel_directory) / wheel_name)
|
|
195
|
+
return final_path.name
|
|
196
|
+
finally:
|
|
197
|
+
_clean_cpp_source()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def build_sdist(sdist_directory, config_settings=None):
|
|
201
|
+
_sync_cpp_source()
|
|
202
|
+
try:
|
|
203
|
+
sdist_name = poetry_api.build_sdist(sdist_directory, config_settings)
|
|
204
|
+
_rewrite_sdist_with_source(Path(sdist_directory) / sdist_name)
|
|
205
|
+
return sdist_name
|
|
206
|
+
finally:
|
|
207
|
+
_clean_cpp_source()
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
from .main import (
|
|
2
|
-
CppkhInterfaceError,
|
|
3
|
-
compile_cppkh,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
1
|
+
from .main import (
|
|
2
|
+
CppkhInterfaceError,
|
|
3
|
+
compile_cppkh,
|
|
4
|
+
compile_cppkh_shared,
|
|
5
|
+
compute_signed_variants,
|
|
6
|
+
compute_many_pd,
|
|
7
|
+
compute_pd,
|
|
8
|
+
get_cppkh_executable,
|
|
9
|
+
normalize_pd_code,
|
|
10
|
+
normalize_pd_codes,
|
|
11
|
+
simplify_pd,
|
|
12
|
+
solve_many_khovanov,
|
|
13
|
+
solve_khovanov,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"CppkhInterfaceError",
|
|
18
|
+
"compile_cppkh",
|
|
19
|
+
"compile_cppkh_shared",
|
|
20
|
+
"compute_signed_variants",
|
|
21
|
+
"compute_many_pd",
|
|
22
|
+
"compute_pd",
|
|
23
|
+
"get_cppkh_executable",
|
|
24
|
+
"normalize_pd_code",
|
|
25
|
+
"normalize_pd_codes",
|
|
26
|
+
"simplify_pd",
|
|
27
|
+
"solve_many_khovanov",
|
|
28
|
+
"solve_khovanov",
|
|
29
|
+
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .main import main
|
|
2
|
-
|
|
3
|
-
if __name__ == "__main__":
|
|
4
|
-
raise SystemExit(main())
|
|
1
|
+
from .main import main
|
|
2
|
+
|
|
3
|
+
if __name__ == "__main__":
|
|
4
|
+
raise SystemExit(main())
|