Skip to content

Commit afcbaba

Browse files
committed
Refactor histogram file processing to ensure temporary files are properly managed and closed
1 parent 9d09355 commit afcbaba

1 file changed

Lines changed: 64 additions & 50 deletions

File tree

src/khisto/core/backend.py

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from __future__ import annotations
88

99
import json
10+
import os
1011
import subprocess
1112
import tempfile
1213
from dataclasses import dataclass, field
@@ -223,54 +224,67 @@ def compute_histograms(x: np.ndarray) -> list[HistogramResult]:
223224
if len(x) == 0:
224225
raise ValueError("Input array is empty after filtering")
225226

226-
with tempfile.NamedTemporaryFile(mode="wb", suffix=".bin") as temp_input_file:
227+
# Use delete=False so the files are closed before the subprocess reads them.
228+
# On Windows, NamedTemporaryFile keeps an exclusive lock while open.
229+
temp_input_file = tempfile.NamedTemporaryFile(
230+
mode="wb", suffix=".bin", delete=False
231+
)
232+
temp_output_file = tempfile.NamedTemporaryFile(
233+
mode="r", suffix=".json", delete=False
234+
)
235+
try:
227236
x.tofile(temp_input_file)
228-
with tempfile.NamedTemporaryFile(mode="r", suffix=".json") as temp_output_file:
229-
cmd = [
230-
str(KHISTO_BIN_DIR),
231-
"-b",
232-
"-e",
233-
"-j",
234-
temp_input_file.name,
235-
temp_output_file.name,
236-
]
237-
try:
238-
subprocess.run(cmd, capture_output=True, text=True, check=True)
239-
except subprocess.CalledProcessError as e:
240-
stdout = e.stdout.strip()
241-
stderr = e.stderr.strip()
242-
details = "\n".join(part for part in (stdout, stderr) if part)
243-
message = _format_runtime_error(
244-
f"khisto failed with exit code {e.returncode}",
245-
cmd,
246-
details or None,
247-
)
248-
logger.error(message)
249-
raise RuntimeError(message) from e
250-
except OSError as e:
251-
message = _format_runtime_error(
252-
"khisto could not be started",
253-
cmd,
254-
str(e),
255-
)
256-
logger.error(message)
257-
raise RuntimeError(message) from e
258-
259-
try:
260-
return _process_histogram_file(temp_output_file.name)
261-
except json.JSONDecodeError as e:
262-
message = _format_runtime_error(
263-
"khisto produced invalid JSON output",
264-
cmd,
265-
str(e),
266-
)
267-
logger.error(message)
268-
raise RuntimeError(message) from e
269-
except (AttributeError, IndexError, TypeError, ValueError) as e:
270-
message = _format_runtime_error(
271-
"khisto produced an invalid histogram payload",
272-
cmd,
273-
str(e),
274-
)
275-
logger.error(message)
276-
raise RuntimeError(message) from e
237+
temp_input_file.close()
238+
temp_output_file.close()
239+
240+
cmd = [
241+
str(KHISTO_BIN_DIR),
242+
"-b",
243+
"-e",
244+
"-j",
245+
temp_input_file.name,
246+
temp_output_file.name,
247+
]
248+
try:
249+
subprocess.run(cmd, capture_output=True, text=True, check=True)
250+
except subprocess.CalledProcessError as e:
251+
stdout = e.stdout.strip()
252+
stderr = e.stderr.strip()
253+
details = "\n".join(part for part in (stdout, stderr) if part)
254+
message = _format_runtime_error(
255+
f"khisto failed with exit code {e.returncode}",
256+
cmd,
257+
details or None,
258+
)
259+
logger.error(message)
260+
raise RuntimeError(message) from e
261+
except OSError as e:
262+
message = _format_runtime_error(
263+
"khisto could not be started",
264+
cmd,
265+
str(e),
266+
)
267+
logger.error(message)
268+
raise RuntimeError(message) from e
269+
270+
try:
271+
return _process_histogram_file(temp_output_file.name)
272+
except json.JSONDecodeError as e:
273+
message = _format_runtime_error(
274+
"khisto produced invalid JSON output",
275+
cmd,
276+
str(e),
277+
)
278+
logger.error(message)
279+
raise RuntimeError(message) from e
280+
except (AttributeError, IndexError, TypeError, ValueError) as e:
281+
message = _format_runtime_error(
282+
"khisto produced an invalid histogram payload",
283+
cmd,
284+
str(e),
285+
)
286+
logger.error(message)
287+
raise RuntimeError(message) from e
288+
finally:
289+
os.unlink(temp_input_file.name)
290+
os.unlink(temp_output_file.name)

0 commit comments

Comments
 (0)