Skip to content

Commit 06769a1

Browse files
committed
test(lmp): defer conversions until runtime guards pass
Move the shared pb conversion calls out of import time so modules can still be collected and skipped cleanly when the required backend is disabled. Also harden the conversion lock handling against stale lock files and switch the conversion subprocess to a non-buffering checked run. Authored by OpenClaw (model: gpt-5.4)
1 parent 549eda6 commit 06769a1

17 files changed

Lines changed: 171 additions & 123 deletions

source/lmp/tests/model_convert.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
22
"""Helpers for preparing converted TensorFlow graph files in LAMMPS tests."""
33

4-
from __future__ import (
5-
annotations,
6-
)
4+
from __future__ import annotations
75

6+
import errno
87
import os
98
import subprocess as sp
109
import sys
1110
import tempfile
1211
import time
13-
from pathlib import (
14-
Path,
15-
)
12+
from pathlib import Path
1613

1714
_LOCK_TIMEOUT_SECONDS = 60.0
1815
_LOCK_POLL_SECONDS = 0.1
@@ -22,13 +19,52 @@ def _is_up_to_date(source: Path, output: Path) -> bool:
2219
return output.exists() and output.stat().st_mtime_ns >= source.stat().st_mtime_ns
2320

2421

22+
def _read_lock_pid(lock_file: Path) -> int | None:
23+
try:
24+
for line in lock_file.read_text(encoding="utf-8").splitlines():
25+
if line.startswith("pid="):
26+
return int(line.split("=", maxsplit=1)[1])
27+
except (FileNotFoundError, ValueError):
28+
return None
29+
return None
30+
31+
32+
def _pid_is_running(pid: int) -> bool:
33+
try:
34+
os.kill(pid, 0)
35+
except ProcessLookupError:
36+
return False
37+
except PermissionError:
38+
return True
39+
except OSError as err:
40+
if err.errno == errno.ESRCH:
41+
return False
42+
raise
43+
return True
44+
45+
46+
def _should_break_stale_lock(lock_file: Path) -> bool:
47+
try:
48+
lock_stat = lock_file.stat()
49+
except FileNotFoundError:
50+
return False
51+
52+
lock_age = time.time() - lock_stat.st_mtime
53+
if lock_age > _LOCK_TIMEOUT_SECONDS:
54+
return True
55+
56+
lock_pid = _read_lock_pid(lock_file)
57+
return lock_pid is not None and not _pid_is_running(lock_pid)
58+
59+
2560
def ensure_converted_pb(source: Path, output: Path) -> Path:
2661
"""Convert ``source`` into ``output`` only when the target is missing or stale.
2762
2863
The conversion is protected by a simple lock file and uses atomic replacement so
2964
repeated imports across multiple test modules do not regenerate the same model
3065
more than once.
3166
"""
67+
3268
source = source.resolve()
3369
output = output.resolve()
3470
output.parent.mkdir(parents=True, exist_ok=True)
@@ -41,6 +77,9 @@ def ensure_converted_pb(source: Path, output: Path) -> Path:
4177
try:
4278
fd = os.open(str(lock_file), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
4379
except FileExistsError as err:
80+
if _should_break_stale_lock(lock_file):
81+
lock_file.unlink(missing_ok=True)
82+
continue
4483
if time.monotonic() - started >= _LOCK_TIMEOUT_SECONDS:
4584
raise TimeoutError(f"Timed out waiting for {lock_file}") from err
4685
time.sleep(_LOCK_POLL_SECONDS)
@@ -61,7 +100,7 @@ def ensure_converted_pb(source: Path, output: Path) -> Path:
61100
)
62101
os.close(tmp_fd)
63102
tmp_path = Path(tmp_name)
64-
sp.check_output(
103+
sp.run(
65104
[
66105
sys.executable,
67106
"-m",
@@ -72,7 +111,8 @@ def ensure_converted_pb(source: Path, output: Path) -> Path:
72111
str(source),
73112
"-o",
74113
str(tmp_path),
75-
]
114+
],
115+
check=True,
76116
)
77117
tmp_path.replace(output)
78118
tmp_path = None

source/lmp/tests/test_deeptensor.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
from lammps import (
1111
PyLammps,
1212
)
13-
from model_convert import (
14-
ensure_converted_pb,
15-
)
1613
from write_lmp_data import (
1714
write_lmp_data,
1815
)
1916

17+
from model_convert import (
18+
ensure_converted_pb,
19+
)
20+
2021
pbtxt_file = Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot.pbtxt"
2122
pb_file = Path(__file__).parent / "graph.pb"
2223
pbtxt_file2 = (
@@ -57,16 +58,14 @@
5758
# type_HO = np.array([2, 1, 1, 2, 1, 1])
5859

5960

60-
ensure_converted_pb(pbtxt_file, pb_file)
61-
62-
ensure_converted_pb(pbtxt_file2, pb_file2)
63-
64-
6561
def setup_module() -> None:
6662
if os.environ.get("ENABLE_TENSORFLOW", "1") != "1":
6763
pytest.skip(
6864
"Skip test because TensorFlow support is not enabled.",
6965
)
66+
ensure_converted_pb(pbtxt_file, pb_file)
67+
ensure_converted_pb(pbtxt_file2, pb_file2)
68+
7069
write_lmp_data(box, coord, type_OH, data_file)
7170
# TODO
7271
# write_lmp_data(box, coord, type_HO, data_type_map_file)

source/lmp/tests/test_dplr.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
from lammps import (
1111
PyLammps,
1212
)
13-
from model_convert import (
14-
ensure_converted_pb,
15-
)
1613
from write_lmp_data import (
1714
write_lmp_data_full,
1815
)
1916

17+
from model_convert import (
18+
ensure_converted_pb,
19+
)
20+
2021
pbtxt_file = Path(__file__).parent / "lrmodel.pbtxt"
2122
pb_file = Path(__file__).parent / "lrmodel.pb"
2223
dipole_pbtxt_file = Path(__file__).parent / "lrdipole.pbtxt"
@@ -266,15 +267,14 @@
266267
mesh = 10
267268

268269

269-
ensure_converted_pb(pbtxt_file, pb_file)
270-
ensure_converted_pb(dipole_pbtxt_file, dipole_pb_file)
271-
272-
273270
def setup_module() -> None:
274271
if os.environ.get("ENABLE_TENSORFLOW", "1") != "1":
275272
pytest.skip(
276273
"Skip test because TensorFlow support is not enabled.",
277274
)
275+
ensure_converted_pb(pbtxt_file, pb_file)
276+
ensure_converted_pb(dipole_pbtxt_file, dipole_pb_file)
277+
278278
write_lmp_data_full(
279279
box, coord, mol_list, type_OH, charge, data_file, bond_list, mass_list
280280
)

source/lmp/tests/test_lammps.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
22
import importlib
33
import os
4-
import shutil
54
import subprocess as sp
5+
import shutil
66
import sys
77
import tempfile
88
from pathlib import (
@@ -15,13 +15,14 @@
1515
from lammps import (
1616
PyLammps,
1717
)
18-
from model_convert import (
19-
ensure_converted_pb,
20-
)
2118
from write_lmp_data import (
2219
write_lmp_data,
2320
)
2421

22+
from model_convert import (
23+
ensure_converted_pb,
24+
)
25+
2526
pbtxt_file = Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot.pbtxt"
2627
pbtxt_file2 = (
2728
Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot-1.pbtxt"
@@ -224,15 +225,14 @@
224225
type_HO = np.array([2, 1, 1, 2, 1, 1])
225226

226227

227-
ensure_converted_pb(pbtxt_file, pb_file)
228-
ensure_converted_pb(pbtxt_file2, pb_file2)
229-
230-
231228
def setup_module() -> None:
232229
if os.environ.get("ENABLE_TENSORFLOW", "1") != "1":
233230
pytest.skip(
234231
"Skip test because TensorFlow support is not enabled.",
235232
)
233+
ensure_converted_pb(pbtxt_file, pb_file)
234+
ensure_converted_pb(pbtxt_file2, pb_file2)
235+
236236
write_lmp_data(box, coord, type_OH, data_file)
237237
write_lmp_data(box, coord, type_HO, data_type_map_file)
238238
write_lmp_data(

source/lmp/tests/test_lammps_3types.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
from lammps import (
1010
PyLammps,
1111
)
12-
from model_convert import (
13-
ensure_converted_pb,
14-
)
1512
from write_lmp_data import (
1613
write_lmp_data,
1714
)
1815

16+
from model_convert import (
17+
ensure_converted_pb,
18+
)
19+
1920
pbtxt_file = Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot.pbtxt"
2021
pbtxt_file2 = (
2122
Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot-1.pbtxt"
@@ -245,15 +246,14 @@
245246
# https://github.com/lammps/lammps/blob/1e1311cf401c5fc2614b5d6d0ff3230642b76597/src/update.cpp#L193
246247
nktv2p = 1.6021765e6
247248

248-
ensure_converted_pb(pbtxt_file, pb_file)
249-
ensure_converted_pb(pbtxt_file2, pb_file2)
250-
251-
252249
def setup_module() -> None:
253250
if os.environ.get("ENABLE_TENSORFLOW", "1") != "1":
254251
pytest.skip(
255252
"Skip test because TensorFlow support is not enabled.",
256253
)
254+
ensure_converted_pb(pbtxt_file, pb_file)
255+
ensure_converted_pb(pbtxt_file2, pb_file2)
256+
257257
write_lmp_data(box, coord, type_OH, data_file)
258258
write_lmp_data(box, coord, type_HO, data_type_map_file)
259259

source/lmp/tests/test_lammps_dpa_jax.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
22
import importlib
33
import os
4-
import shutil
54
import subprocess as sp
5+
import shutil
66
import sys
77
import tempfile
88
from pathlib import (
@@ -15,13 +15,14 @@
1515
from lammps import (
1616
PyLammps,
1717
)
18-
from model_convert import (
19-
ensure_converted_pb,
20-
)
2118
from write_lmp_data import (
2219
write_lmp_data,
2320
)
2421

22+
from model_convert import (
23+
ensure_converted_pb,
24+
)
25+
2526
pbtxt_file2 = (
2627
Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot-1.pbtxt"
2728
)
@@ -225,14 +226,14 @@
225226
type_HO = np.array([2, 1, 1, 2, 1, 1])
226227

227228

228-
ensure_converted_pb(pbtxt_file2, pb_file2)
229-
230-
231229
def setup_module():
232230
if os.environ.get("ENABLE_JAX", "1") != "1":
233231
pytest.skip(
234232
"Skip test because JAX support is not enabled.",
235233
)
234+
if os.environ.get("ENABLE_TENSORFLOW", "1") == "1":
235+
ensure_converted_pb(pbtxt_file2, pb_file2)
236+
236237
write_lmp_data(box, coord, type_OH, data_file)
237238
write_lmp_data(box, coord, type_HO, data_type_map_file)
238239
write_lmp_data(

source/lmp/tests/test_lammps_dpa_pt.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
22
import importlib
33
import os
4-
import shutil
54
import subprocess as sp
5+
import shutil
66
import sys
77
import tempfile
88
from pathlib import (
@@ -15,13 +15,14 @@
1515
from lammps import (
1616
PyLammps,
1717
)
18-
from model_convert import (
19-
ensure_converted_pb,
20-
)
2118
from write_lmp_data import (
2219
write_lmp_data,
2320
)
2421

22+
from model_convert import (
23+
ensure_converted_pb,
24+
)
25+
2526
pbtxt_file2 = (
2627
Path(__file__).parent.parent.parent / "tests" / "infer" / "deeppot-1.pbtxt"
2728
)
@@ -223,14 +224,14 @@
223224
type_HO = np.array([2, 1, 1, 2, 1, 1])
224225

225226

226-
ensure_converted_pb(pbtxt_file2, pb_file2)
227-
228-
229227
def setup_module() -> None:
230228
if os.environ.get("ENABLE_PYTORCH", "1") != "1":
231229
pytest.skip(
232230
"Skip test because PyTorch support is not enabled.",
233231
)
232+
if os.environ.get("ENABLE_TENSORFLOW", "1") == "1":
233+
ensure_converted_pb(pbtxt_file2, pb_file2)
234+
234235
write_lmp_data(box, coord, type_OH, data_file)
235236
write_lmp_data(box, coord, type_HO, data_type_map_file)
236237
write_lmp_data(

0 commit comments

Comments
 (0)