From 4adafc8417d1aa75345e5e52b3ae594c3b099bec Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Thu, 31 Aug 2023 14:58:27 +0200
Subject: [PATCH] Improve temporary file handling

---
 src/calng/CrystfelRunner.py | 40 ++++++++++++++++---------------------
 1 file changed, 17 insertions(+), 23 deletions(-)

diff --git a/src/calng/CrystfelRunner.py b/src/calng/CrystfelRunner.py
index 48401f7d..086db51c 100644
--- a/src/calng/CrystfelRunner.py
+++ b/src/calng/CrystfelRunner.py
@@ -1,7 +1,9 @@
 import itertools
+import os
 import queue
 import re
 import subprocess
+import tempfile
 import threading
 import time
 
@@ -386,6 +388,12 @@ class CrystfelRunner(PythonDevice):
         self.startCrystfel()
 
     def startCrystfel(self):
+        if hasattr(self, "_temporary_stream_fd"):
+            # if we made up a temporary file already and stopped CrystFEL
+            if self.get("crystfelArgs.streamOutputPath") == self._temporary_stream_fd.name:
+                # then forget about it so we can make a new one
+                self.set("crystfelArgs.streamOutputPath", "")
+                delattr(self, "_temporary_stream_fd")
         self._run_crystfel()
         self.updateState(State.STARTED)
 
@@ -407,7 +415,6 @@ class CrystfelRunner(PythonDevice):
         fashion). The argument should be a hash (via WebProxy, a JSON-encoded dict)
         with a list of strings (with newline characters already there) under
         'fileContents'."""
-        import tempfile
         fd = tempfile.NamedTemporaryFile(mode="w")
         fd.writelines(data["fileContents"])
         fd.flush()
@@ -516,7 +523,6 @@ class CrystfelRunner(PythonDevice):
         threading.Thread(target=server, daemon=True).start()
 
     def _run_crystfel(self):
-        import os
         env = os.environ.copy()
         env["PATH"] = (
             self.get("crystfelArgs.crystfelBinPath") +
@@ -524,21 +530,18 @@ class CrystfelRunner(PythonDevice):
         )
         # we must have a stream output!
         # if empty, set it to something and delete after we are done reading it
-        streamOutputPath = self.get("crystfelArgs.streamOutputPath")
-        deleteOutput = False
-        if len(streamOutputPath) == 0:
-            # TODO: Is /tmp ok?
-            # the goal is only to prevent
-            name = self.get("deviceId").replace("/", "_")
-            streamOutputPath = f"/tmp/out_{name}.stream"
-            deleteOutput = True
+        if not self.get("crystfelArgs.streamOutputPath"):
+            stream_fd = tempfile.NamedTemporaryFile()
+            self._temporary_stream_fd = stream_fd
+            self.set("crystfelArgs.streamOutputPath", stream_fd.name)
+        stream_fn = self.get("crystfelArgs.streamOutputPath")
         args = [
             "indexamajig",
             f"--zmq-input=tcp://localhost:{self.get('crystfelArgs.zmqPort')}",
             "--zmq-request=next",
             "--data-format=msgpack",
             "-o",
-            streamOutputPath,
+            stream_fn,
             "-g",
             self.get("crystfelArgs.geometryPath"),
         ]
@@ -552,8 +555,7 @@ class CrystfelRunner(PythonDevice):
         # Somehow Karabo substitutes ["--int-radius=3,4,5"] with ["--int-radius=3", "4", "5"] for Danilo
         # and it adds \\ escape sequences for David H.
         # both of those "effects" are handled in the hack function below
-        fixed_misc = reparse_fix_comma_mess(self.get("crystfelArgs.misc"))
-        args += fixed_misc
+        args.extend(reparse_fix_comma_mess(self.get("crystfelArgs.misc")))
 
         # this should override the --peaks option set by the user, if they
         # want to use an external peakfinder
@@ -586,7 +588,7 @@ class CrystfelRunner(PythonDevice):
 
         ## do something with stream file
         #self._tail_proc = subprocess.Popen(
-        #    ["tail", "-F", "-n", "+0", streamOutputPath],
+        #    ["tail", "-F", "-n", "+0", stream_fn],
         #    stdout=subprocess.PIPE,
         #    stderr=subprocess.PIPE,
         #    shell=False,
@@ -619,7 +621,7 @@ class CrystfelRunner(PythonDevice):
                 n_reflections = list()
                 cell = list()
                 det_centre = list()
-                with open(streamOutputPath, "r") as outFile:
+                with open(stream_fn, "r") as outFile:
                     #for line in self._tail_proc.stdout:
                     for line in outFile:
                         if reading_chunk:
@@ -669,14 +671,6 @@ class CrystfelRunner(PythonDevice):
                     f"crystfelStats", Hash(*itertools.chain.from_iterable(results.items()))
                 )
 
-                # ... and delete output stream file if requested:
-                if deleteOutput:
-                    try:
-                        os.remove(streamOutputPath)
-                    except:
-                        pass
-
         threading.Thread(target=collect_details, daemon=True).start()
 
 utils.add_unsafe_get(CrystfelRunner)
-
-- 
GitLab