Skip to content

Commit 98c126c

Browse files
authored
Merge pull request #84 from kif/83_close_files
83 close files
2 parents 27eb84f + 789dd60 commit 98c126c

File tree

14 files changed

+216
-156
lines changed

14 files changed

+216
-156
lines changed

Diff for: plugins/bm29/common.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
__contact__ = "[email protected]"
1212
__license__ = "MIT"
1313
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
14-
__date__ = "03/12/2024"
14+
__date__ = "20/02/2025"
1515
__status__ = "development"
1616
version = "0.0.2"
1717

@@ -71,7 +71,9 @@ def get_integrator(keycache):
7171
ai = pyFAI.load(keycache.poni)
7272
ai.wavelength = 1e-10 * pyFAI.units.hc / keycache.energy
7373
if keycache.mask:
74-
mask = numpy.logical_or(fabio.open(keycache.mask).data, ai.detector.mask).astype("int8")
74+
with fabio.open(keycache.mask) as fimg:
75+
fabio_mask = fimg.data
76+
mask = numpy.logical_or(fabio_mask, ai.detector.mask).astype("int8")
7577
ai.detector.mask = mask
7678
shared_cache[keycache] = ai
7779
return ai

Diff for: plugins/bm29/ispyb.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,15 @@ def send_icat(self, proposal=None, beamline=None, sample=None, dataset=None, pat
105105
:param path: directory name where processed data are staying
106106
:param raw: directory name of the raw data (not the processed ones)
107107
:param data: dict with all data sent to ISpyB
108-
109108
"""
110109
tmp = self.gallery.strip("/").split("/")
111110
idx_process = [i for i,j in enumerate(tmp) if j.lower().startswith("process")][-1]
112-
if tmp[idx_process] == "process":
113-
assert idx_process>5
111+
if tmp[idx_process] == "processed":
112+
assert idx_process>=6
114113
if proposal is None:
115-
proposal = tmp[idx_process-5]
114+
proposal = tmp[idx_process-6]
116115
if beamline is None:
117-
beamline = tmp[idx_process-4]
116+
beamline = tmp[idx_process-5]
118117
if sample is None:
119118
sample = tmp[idx_process-2]
120119
if dataset is None:

Diff for: plugins/bm29/nexus.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""Module for writing HDF5 in the Nexus style"""
22

3-
__author__ = "Jerome Kieffer"
3+
__author__ = "Jérôme Kieffer"
44
__contact__ = "[email protected]"
55
__license__ = "MIT"
66
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
7-
__date__ = "21/04/2022"
7+
__date__ = "20/02/2025"
88
__status__ = "production"
99
__docformat__ = 'restructuredtext'
1010

@@ -13,6 +13,7 @@
1313
import time
1414
import logging
1515
import h5py
16+
import atexit
1617
logger = logging.getLogger(__name__)
1718

1819

@@ -45,7 +46,7 @@ def from_isotime(text, use_tz=False):
4546
text = str(text)
4647
if len(text) < 19:
4748
logger.warning("Not a iso-time string: %s", text)
48-
return None
49+
return
4950
base = text[:19]
5051
if use_tz and len(text) == 25:
5152
sgn = 1 if text[:19] == "+" else -1
@@ -85,14 +86,18 @@ class Nexus:
8586
TODO: make it thread-safe !!!
8687
"""
8788

88-
def __init__(self, filename, mode=None, creator=None, timeout=None):
89+
def __init__(self, filename, mode=None,
90+
creator=None,
91+
timeout=None,
92+
start_time=None):
8993
"""
9094
Constructor
9195
9296
:param filename: name of the hdf5 file containing the nexus
9397
:param mode: can be 'r', 'a', 'w', '+' ....
9498
:param creator: set as attr of the NXroot
9599
:param timeout: retry for that amount of time (in seconds)
100+
:param start_time: set as attr of the NXroot
96101
"""
97102
self.filename = os.path.abspath(filename)
98103
self.mode = mode
@@ -132,10 +137,10 @@ def __init__(self, filename, mode=None, creator=None, timeout=None):
132137
self.file_handle = None
133138
self.h5 = h5py.File(self.filename, mode=self.mode)
134139
self.to_close = []
135-
140+
atexit.register(self.close)
136141
if not pre_existing:
137142
self.h5.attrs["NX_class"] = "NXroot"
138-
self.h5.attrs["file_time"] = get_isotime()
143+
self.h5.attrs["file_time"] = get_isotime(start_time)
139144
self.h5.attrs["file_name"] = self.filename
140145
self.h5.attrs["HDF5_Version"] = h5py.version.hdf5_version
141146
self.h5.attrs["creator"] = creator or self.__class__.__name__
@@ -178,8 +183,7 @@ def get_entry(self, name):
178183
if isinstance(grp, h5py.Group) and \
179184
("start_time" in grp) and \
180185
self.get_attr(grp, "NX_class") == "NXentry":
181-
return grp
182-
return None
186+
return grp
183187

184188
def get_entries(self):
185189
"""
@@ -212,7 +216,8 @@ def find_detector(self, all=False):
212216
return result
213217

214218
def new_entry(self, entry="entry", program_name="pyFAI",
215-
title=None, force_time=None, force_name=False):
219+
title="description of experiment",
220+
force_time=None, force_name=False):
216221
"""
217222
Create a new entry
218223

Diff for: plugins/id02/nexus.py

+58-17
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
__contact__ = "[email protected]"
55
__license__ = "MIT"
66
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
7-
__date__ = "29/01/2024"
7+
__date__ = "20/02/2025"
88
__status__ = "production"
99
__docformat__ = 'restructuredtext'
1010

@@ -13,6 +13,7 @@
1313
import time
1414
import logging
1515
import h5py
16+
import atexit
1617
logger = logging.getLogger(__name__)
1718

1819

@@ -70,7 +71,7 @@ def is_hdf5(filename):
7071
return sig == signature
7172

7273

73-
class Nexus(object):
74+
class Nexus:
7475
"""
7576
Writer class to handle Nexus/HDF5 data
7677
@@ -85,13 +86,17 @@ class Nexus(object):
8586
TODO: make it thread-safe !!!
8687
"""
8788

88-
def __init__(self, filename, mode=None, creator=None, start_time=None):
89+
def __init__(self, filename, mode=None,
90+
creator=None,
91+
timeout=None,
92+
start_time=None):
8993
"""
9094
Constructor
9195
9296
:param filename: name of the hdf5 file containing the nexus
9397
:param mode: can be 'r', 'a', 'w', '+' ....
9498
:param creator: set as attr of the NXroot
99+
:param timeout: retry for that amount of time (in seconds)
95100
:param start_time: set as attr of the NXroot
96101
"""
97102
self.filename = os.path.abspath(filename)
@@ -107,15 +112,33 @@ def __init__(self, filename, mode=None, creator=None, start_time=None):
107112
else:
108113
self.mode = "a"
109114

110-
if self.mode == "r" and h5py.version.version_tuple >= (2, 9):
111-
self.file_handle = open(self.filename, mode=self.mode + "b")
112-
self.h5 = h5py.File(self.file_handle, mode=self.mode)
115+
if timeout:
116+
end = time.perf_counter() + timeout
117+
while time.perf_counter() < end :
118+
try:
119+
if self.mode == "r":
120+
self.file_handle = open(self.filename, mode="rb")
121+
self.h5 = h5py.File(self.file_handle, mode="r")
122+
else:
123+
self.file_handle = None
124+
self.h5 = h5py.File(self.filename, mode=self.mode)
125+
except OSError:
126+
os.stat(os.path.dirname(self.filename))
127+
time.sleep(1)
128+
else:
129+
break
130+
else:
131+
raise OSError(f"Unable to open HDF5 file {self.filename}")
113132
else:
114-
self.file_handle = None
115-
self.h5 = h5py.File(self.filename, mode=self.mode)
133+
if self.mode == "r":
134+
self.file_handle = open(self.filename, mode=self.mode + "b")
135+
self.h5 = h5py.File(self.file_handle, mode=self.mode)
136+
else:
137+
self.file_handle = None
138+
self.h5 = h5py.File(self.filename, mode=self.mode)
116139
self.to_close = []
117-
118-
if not pre_existing or "w" in mode:
140+
atexit.register(self.close)
141+
if not pre_existing:
119142
self.h5.attrs["NX_class"] = "NXroot"
120143
self.h5.attrs["file_time"] = get_isotime(start_time)
121144
self.h5.attrs["file_name"] = self.filename
@@ -143,6 +166,7 @@ def __exit__(self, *arg, **kwarg):
143166
self.close()
144167

145168
def flush(self):
169+
"write to disk"
146170
if self.h5:
147171
self.h5.flush()
148172

@@ -199,7 +223,7 @@ def new_entry(self, entry="entry", program_name="pyFAI",
199223
200224
:param entry: name of the entry
201225
:param program_name: value of the field as string
202-
:param title: value of the field as string
226+
:param title: description of experiment as str
203227
:param force_time: enforce the start_time (as string!)
204228
:param force_name: force the entry name as such, without numerical suffix.
205229
:return: the corresponding HDF5 group
@@ -211,7 +235,7 @@ def new_entry(self, entry="entry", program_name="pyFAI",
211235
entry_grp = self.h5.require_group(entry)
212236
self.h5.attrs["default"] = entry
213237
entry_grp.attrs["NX_class"] = "NXentry"
214-
entry_grp["title"] = title
238+
entry_grp["title"] = str(title)
215239
entry_grp["program_name"] = program_name
216240
if isinstance(force_time, str):
217241
entry_grp["start_time"] = force_time
@@ -230,16 +254,18 @@ def new_instrument(self, entry="entry", instrument_name="id00",):
230254
# howto external link
231255
# myfile['ext link'] = h5py.ExternalLink("otherfile.hdf5", "/path/to/resource")
232256

233-
def new_class(self, grp, name, class_type="NXcollection"):
257+
@staticmethod
258+
def new_class(grp, name, class_type="NXcollection"):
234259
"""
235260
create a new sub-group with type class_type
261+
236262
:param grp: parent group
237263
:param name: name of the sub-group
238264
:param class_type: NeXus class name
239265
:return: subgroup created
240266
"""
241267
sub = grp.require_group(name)
242-
sub.attrs["NX_class"] = class_type
268+
sub.attrs["NX_class"] = str(class_type)
243269
return sub
244270

245271
def new_detector(self, name="detector", entry="entry", subentry="pyFAI"):
@@ -253,8 +279,8 @@ def new_detector(self, name="detector", entry="entry", subentry="pyFAI"):
253279
from . import __version__ as version
254280
entry_grp = self.new_entry(entry)
255281
pyFAI_grp = self.new_class(entry_grp, subentry, "NXsubentry")
256-
pyFAI_grp["definition_local"] = "pyFAI"
257-
pyFAI_grp["definition_local"].attrs["version"] = version
282+
pyFAI_grp["definition_local"] = str("pyFAI")
283+
pyFAI_grp["definition_local"].attrs["version"] = str(version)
258284
det_grp = self.new_class(pyFAI_grp, name, "NXdetector")
259285
return det_grp
260286

@@ -282,6 +308,21 @@ def get_data(self, grp, attr=None, value=None):
282308
self.get_attr(grp[name], attr) == value]
283309
return coll
284310

311+
def get_default_NXdata(self):
312+
"""Return the default plot configured in the nexus structure.
313+
314+
:return: the group with the default plot or None if not found
315+
"""
316+
entry_name = self.h5.attrs.get("default")
317+
if entry_name:
318+
entry_grp = self.h5.get(entry_name)
319+
nxdata_name = entry_grp.attrs.get("default")
320+
if nxdata_name:
321+
if nxdata_name.startswith("/"):
322+
return self.h5.get(nxdata_name)
323+
return entry_grp.get(nxdata_name)
324+
return None
325+
285326
def deep_copy(self, name, obj, where="/", toplevel=None, excluded=None, overwrite=False):
286327
"""
287328
perform a deep copy:
@@ -300,7 +341,7 @@ def deep_copy(self, name, obj, where="/", toplevel=None, excluded=None, overwrit
300341
if name not in toplevel:
301342
grp = toplevel.require_group(name)
302343
for k, v in obj.attrs.items():
303-
grp.attrs[k] = v
344+
grp.attrs[k] = v
304345
elif isinstance(obj, h5py.Dataset):
305346
if name in toplevel:
306347
if overwrite:

Diff for: plugins/id02/single_detector.py

+34-35
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
__contact__ = "[email protected]"
99
__license__ = "MIT"
1010
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
11-
__date__ = "14/02/2024"
11+
__date__ = "20/02/2025"
1212
__status__ = "development"
1313
__version__ = "0.9.3"
1414

@@ -371,20 +371,20 @@ def process(self):
371371
if self.flat_filename.endswith(".h5") or self.flat_filename.endswith(".nxs") or self.flat_filename.endswith(".hdf5"):
372372
flat = self.read_data(self.flat_filename)
373373
else:
374-
flat_fabio = fabio.open(self.flat_filename)
375-
flat = flat_fabio.data
376-
dummy = flat_fabio.header.get("Dummy")
377-
try:
378-
dummy = float(dummy)
379-
except:
380-
self.log_error("Dummy value in mask is unconsistent %s" % dummy)
381-
dummy = None
382-
ddummy = flat_fabio.header.get("DDummy")
383-
try:
384-
ddummy = float(ddummy)
385-
except:
386-
self.log_error("DDummy value in mask is unconsitent %s" % ddummy)
387-
ddummy = 0
374+
with fabio.open(self.flat_filename) as flat_fabio:
375+
flat = flat_fabio.data
376+
dummy = flat_fabio.header.get("Dummy")
377+
try:
378+
dummy = float(dummy)
379+
except:
380+
self.log_error("Dummy value in mask is unconsistent %s" % dummy)
381+
dummy = None
382+
ddummy = flat_fabio.header.get("DDummy")
383+
try:
384+
ddummy = float(ddummy)
385+
except:
386+
self.log_error("DDummy value in mask is unconsitent %s" % ddummy)
387+
ddummy = 0
388388

389389
if flat.ndim == 3:
390390
self.flat = pyFAI.average.average_dark(flat, center_method="median")
@@ -415,28 +415,27 @@ def process(self):
415415
self.mask_filename = self.input.get("regrouping_mask_filename")
416416
if isinstance(self.mask_filename, StringTypes) and os.path.exists(self.mask_filename):
417417
try:
418-
mask_fabio = fabio.open(self.mask_filename)
418+
with fabio.open(self.mask_filename) as mask_fabio:
419+
dummy = mask_fabio.header.get("Dummy")
420+
try:
421+
dummy = float(dummy)
422+
except:
423+
self.log_error("Dummy value in mask is unconsitent %s" % dummy)
424+
dummy = None
425+
ddummy = mask_fabio.header.get("DDummy")
426+
try:
427+
ddummy = float(ddummy)
428+
except:
429+
self.log_error("DDummy value in mask is unconsitent %s" % ddummy)
430+
ddummy = 0
431+
if ddummy:
432+
local_mask = abs(mask_fabio.data - dummy) <= ddummy
433+
else:
434+
local_mask = mask_fabio.data == dummy
435+
self.dummy = dummy
436+
self.delta_dummy = ddummy
419437
except:
420438
local_mask = self.read_data(self.mask_filename) != 0
421-
else: # this is very ID02 specific !!!!
422-
dummy = mask_fabio.header.get("Dummy")
423-
try:
424-
dummy = float(dummy)
425-
except:
426-
self.log_error("Dummy value in mask is unconsitent %s" % dummy)
427-
dummy = None
428-
ddummy = mask_fabio.header.get("DDummy")
429-
try:
430-
ddummy = float(ddummy)
431-
except:
432-
self.log_error("DDummy value in mask is unconsitent %s" % ddummy)
433-
ddummy = 0
434-
if ddummy:
435-
local_mask = abs(mask_fabio.data - dummy) <= ddummy
436-
else:
437-
local_mask = mask_fabio.data == dummy
438-
self.dummy = dummy
439-
self.delta_dummy = ddummy
440439
if local_mask.ndim == 3:
441440
local_mask = pyFAI.average.average_dark(local_mask, center_method="median")
442441
if (local_mask is not None) and (local_mask.shape != shape):

0 commit comments

Comments
 (0)