ewoksdata 2.3.0rc3__tar.gz → 2.3.0rc4__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.
- {ewoksdata-2.3.0rc3/src/ewoksdata.egg-info → ewoksdata-2.3.0rc4}/PKG-INFO +1 -1
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/pyproject.toml +1 -1
- ewoksdata-2.3.0rc4/src/ewoksdata/data/hdf5/overwrite.py +102 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/utils.py +14 -1
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_dataset_writer.py +67 -37
- ewoksdata-2.3.0rc4/src/ewoksdata/tests/unit/test_overwrite_hdf5.py +143 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4/src/ewoksdata.egg-info}/PKG-INFO +1 -1
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata.egg-info/SOURCES.txt +2 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/LICENSE.md +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/README.md +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/setup.cfg +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/bliss.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/blissdata/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/blissdata/_lima.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/blissdata/blissdatav0.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/blissdata/blissdatav1.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/blissdata/blissdatav2.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/contextiterator.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/config.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_base.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_non_sequential.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_non_sequential_stack.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_sequential.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_sequential_stack.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/mirror.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/types.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/nexus.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/url.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/integration/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/integration/conftest.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/integration/test_blissdata.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/conftest.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/data/__init__.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/data/bliss_scans.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_contextiterator.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_data_bliss.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_data_hdf5.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_data_nexus.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_find_lima_scans.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/tests/unit/test_mirror_hdf5.py +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata.egg-info/dependency_links.txt +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata.egg-info/requires.txt +0 -0
- {ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ewoksdata"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.0rc4"
|
|
8
8
|
authors = [{ name = "ESRF", email = "dau-pydev@esrf.fr" }]
|
|
9
9
|
description = "Ewoks tasks for data access"
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
4
|
+
import uuid
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import List
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
from silx.io import h5py_utils
|
|
11
|
+
from silx.io.url import DataUrl
|
|
12
|
+
|
|
13
|
+
from ..url import as_dataurl
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def ensure_items_do_not_exist(
|
|
17
|
+
*urls: Union[str, DataUrl], delete_existing: bool = False
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Make sure the HDF5 items (groups, datasets or links) do not exist.
|
|
20
|
+
|
|
21
|
+
Existing items are deleted when :code:`delete_existing` is :code:`True`.
|
|
22
|
+
A file is removed when its root group is deleted or nothing remains,
|
|
23
|
+
and repacked otherwise.
|
|
24
|
+
|
|
25
|
+
:raises FileExistsError: when items exist and :code:`delete_existing` is :code:`False`.
|
|
26
|
+
:raises ValueError: when an item to delete is in another file (external link).
|
|
27
|
+
"""
|
|
28
|
+
data_paths: Dict[str, List[str]] = defaultdict(list)
|
|
29
|
+
for url in urls:
|
|
30
|
+
url = as_dataurl(url)
|
|
31
|
+
filename = url.file_path()
|
|
32
|
+
parts = [s for s in (url.data_path() or "").split("/") if s]
|
|
33
|
+
data_paths[filename].append("/".join(parts) or "/")
|
|
34
|
+
for filename, file_data_paths in data_paths.items():
|
|
35
|
+
_ensure_items_do_not_exist(filename, file_data_paths, delete_existing)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _ensure_items_do_not_exist(
|
|
39
|
+
filename: str, data_paths: List[str], delete_existing: bool
|
|
40
|
+
) -> None:
|
|
41
|
+
""":code:`ensure_items_do_not_exist` for the items of one file."""
|
|
42
|
+
if not os.path.exists(filename):
|
|
43
|
+
return
|
|
44
|
+
if "/" in data_paths:
|
|
45
|
+
if not delete_existing:
|
|
46
|
+
raise FileExistsError(f"HDF5 file {filename!r} already exists")
|
|
47
|
+
os.remove(filename)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
mode = "a" if delete_existing else "r"
|
|
51
|
+
with h5py_utils.File(filename, mode=mode) as root:
|
|
52
|
+
existing = [data_path for data_path in data_paths if data_path in root]
|
|
53
|
+
if not existing:
|
|
54
|
+
return
|
|
55
|
+
if not delete_existing:
|
|
56
|
+
raise FileExistsError(
|
|
57
|
+
f"HDF5 items already exist in {filename!r}: {existing}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
real_filename = os.path.realpath(root.filename)
|
|
61
|
+
for data_path in existing:
|
|
62
|
+
parent_path = data_path.rpartition("/")[0]
|
|
63
|
+
if not parent_path:
|
|
64
|
+
continue
|
|
65
|
+
parent_filename = os.path.realpath(root[parent_path].file.filename)
|
|
66
|
+
if parent_filename != real_filename:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"HDF5 item {data_path!r} of {filename!r} is in another file"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for data_path in existing:
|
|
72
|
+
# Can be gone already when a parent was deleted
|
|
73
|
+
if data_path in root:
|
|
74
|
+
del root[data_path]
|
|
75
|
+
remove_file = len(root) == 0
|
|
76
|
+
|
|
77
|
+
if remove_file:
|
|
78
|
+
os.remove(filename)
|
|
79
|
+
else:
|
|
80
|
+
# Deleting does not free the space in the file
|
|
81
|
+
_repack(filename)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _repack(filename: str) -> None:
|
|
85
|
+
"""Rewrite the file without its unused space."""
|
|
86
|
+
dirname = os.path.dirname(os.path.abspath(filename))
|
|
87
|
+
with tempfile.TemporaryDirectory(dir=dirname) as tmp_dirname:
|
|
88
|
+
tmp_filename = os.path.join(tmp_dirname, os.path.basename(filename))
|
|
89
|
+
with h5py_utils.File(filename, mode="r") as src:
|
|
90
|
+
with h5py_utils.File(tmp_filename, mode="w") as dst:
|
|
91
|
+
# The root cannot be copied onto the root of another file. Copy it
|
|
92
|
+
# in one call to a temporary group so that hard links between items
|
|
93
|
+
# stay shared. Deleting that group leaves its header and link index
|
|
94
|
+
# unused: about 1 KB plus 9 bytes per root item, not data.
|
|
95
|
+
tmp_name = f"_repack_{uuid.uuid4().hex}"
|
|
96
|
+
src.copy(src, dst, name=tmp_name)
|
|
97
|
+
for name in list(dst[tmp_name]):
|
|
98
|
+
dst.move(f"{tmp_name}/{name}", name)
|
|
99
|
+
del dst[tmp_name]
|
|
100
|
+
dst.attrs.update(src.attrs)
|
|
101
|
+
shutil.copymode(filename, tmp_filename)
|
|
102
|
+
os.replace(tmp_filename, filename)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from numbers import Number
|
|
2
3
|
from typing import List
|
|
3
4
|
from typing import Mapping
|
|
@@ -16,6 +17,18 @@ def is_data(data):
|
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def data_from_storage(data, remove_numpy=True):
|
|
20
|
+
"""Deprecated: values restored from ewoks persistent storage no longer need to be
|
|
21
|
+
converted."""
|
|
22
|
+
warnings.warn(
|
|
23
|
+
"'data_from_storage' is deprecated. Values restored from ewoks persistent "
|
|
24
|
+
"storage keep their python type since ewokscore 5.1.0rc4.",
|
|
25
|
+
DeprecationWarning,
|
|
26
|
+
stacklevel=2,
|
|
27
|
+
)
|
|
28
|
+
return _data_from_storage(data, remove_numpy=remove_numpy)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _data_from_storage(data, remove_numpy=True):
|
|
19
32
|
if isinstance(data, numpy.ndarray):
|
|
20
33
|
if not remove_numpy:
|
|
21
34
|
return data
|
|
@@ -25,7 +38,7 @@ def data_from_storage(data, remove_numpy=True):
|
|
|
25
38
|
return data.tolist()
|
|
26
39
|
elif isinstance(data, Mapping):
|
|
27
40
|
return {
|
|
28
|
-
k:
|
|
41
|
+
k: _data_from_storage(v, remove_numpy=remove_numpy)
|
|
29
42
|
for k, v in data.items()
|
|
30
43
|
if not k.startswith("@")
|
|
31
44
|
}
|
|
@@ -191,9 +191,19 @@ def test_concurrent_reader(tmp_path):
|
|
|
191
191
|
|
|
192
192
|
mp_context = multiprocessing.get_context("spawn")
|
|
193
193
|
start_event = mp_context.Event()
|
|
194
|
+
done_event = mp_context.Event()
|
|
195
|
+
nread = mp_context.Value("i", 0)
|
|
194
196
|
reader_process = mp_context.Process(
|
|
195
197
|
target=_read_hdf5_file,
|
|
196
|
-
args=(
|
|
198
|
+
args=(
|
|
199
|
+
hdf5_filename,
|
|
200
|
+
dataset_name,
|
|
201
|
+
npoints,
|
|
202
|
+
read_timestamps,
|
|
203
|
+
start_event,
|
|
204
|
+
done_event,
|
|
205
|
+
nread,
|
|
206
|
+
),
|
|
197
207
|
)
|
|
198
208
|
reader_process.start()
|
|
199
209
|
|
|
@@ -201,80 +211,100 @@ def test_concurrent_reader(tmp_path):
|
|
|
201
211
|
|
|
202
212
|
# Run write loop
|
|
203
213
|
try:
|
|
204
|
-
_write_hdf5_file(hdf5_filename, dataset_name, npoints, write_timestamps)
|
|
214
|
+
_write_hdf5_file(hdf5_filename, dataset_name, npoints, write_timestamps, nread)
|
|
205
215
|
except Exception:
|
|
206
216
|
reader_process.terminate()
|
|
207
217
|
raise
|
|
208
218
|
|
|
209
|
-
#
|
|
219
|
+
# Do not rely on the exit code: with gevent monkey-patching (bliss
|
|
220
|
+
# integration tests in the same session), once any subprocess was started,
|
|
221
|
+
# gevent collects the exit code of every child process that terminates.
|
|
222
|
+
# The exit code of the reader is then no longer available to
|
|
223
|
+
# multiprocessing, so `reader_process.exitcode` stays `None`.
|
|
210
224
|
try:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
f"Reader process terminated with exit code {reader_process.exitcode}."
|
|
217
|
-
)
|
|
218
|
-
except Exception:
|
|
219
|
-
reader_process.terminate()
|
|
220
|
-
raise
|
|
225
|
+
assert done_event.wait(timeout=20), "Reader did not finish within the timeout."
|
|
226
|
+
finally:
|
|
227
|
+
reader_process.join(timeout=5)
|
|
228
|
+
if reader_process.is_alive():
|
|
229
|
+
reader_process.terminate()
|
|
221
230
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
read_events = [int(line.strip()) for line in eventfile.readlines()]
|
|
225
|
-
with open(write_timestamps, "r") as eventfile:
|
|
226
|
-
write_events = [int(line.strip()) for line in eventfile.readlines()]
|
|
231
|
+
read_events = _load_events(read_timestamps)
|
|
232
|
+
write_events = _load_events(write_timestamps)
|
|
227
233
|
|
|
228
|
-
assert
|
|
229
|
-
assert
|
|
234
|
+
assert [value for _, value in read_events] == list(range(npoints))
|
|
235
|
+
assert [value for _, value in write_events] == list(range(npoints))
|
|
230
236
|
|
|
231
|
-
|
|
232
|
-
assert read_events
|
|
237
|
+
# The reader got data before the writer finished
|
|
238
|
+
assert read_events[0][0] < write_events[-1][0], (
|
|
233
239
|
"reads and writes are not interleaved"
|
|
234
240
|
)
|
|
235
241
|
|
|
236
242
|
|
|
237
243
|
class _DatasetWriter(dataset_writer.DatasetWriter):
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
"""Waits after each flush until the reader has read all flushed points."""
|
|
245
|
+
|
|
246
|
+
def __init__(self, *args, nread, **kwargs) -> None:
|
|
247
|
+
super().__init__(*args, **kwargs)
|
|
248
|
+
self._nread = nread
|
|
242
249
|
|
|
243
250
|
def _flush_hdf5(self) -> None:
|
|
244
251
|
print("flush", flush=True)
|
|
245
252
|
super()._flush_hdf5()
|
|
246
|
-
|
|
253
|
+
# Timing alone does not guarantee interleaving on a loaded machine
|
|
254
|
+
nflushed = self.dataset.shape[0]
|
|
255
|
+
t0 = time.monotonic()
|
|
256
|
+
while self._nread.value < nflushed and time.monotonic() - t0 < 10:
|
|
257
|
+
time.sleep(0.01)
|
|
247
258
|
|
|
248
259
|
|
|
249
|
-
def _write_hdf5_file(hdf5_filename, dataset_name, npoints, event_filename):
|
|
250
|
-
data = numpy.zeros((500, 500), dtype=numpy.int64)
|
|
260
|
+
def _write_hdf5_file(hdf5_filename, dataset_name, npoints, event_filename, nread):
|
|
251
261
|
with open(event_filename, "a") as eventfile:
|
|
252
262
|
with h5py.File(hdf5_filename, mode="w") as h5file:
|
|
253
|
-
with _DatasetWriter(
|
|
263
|
+
with _DatasetWriter(
|
|
264
|
+
h5file, dataset_name, flush_period=0.1, nread=nread
|
|
265
|
+
) as writer:
|
|
254
266
|
for i in range(npoints):
|
|
255
|
-
writer.add_point(
|
|
256
|
-
eventfile.write(f"{time.perf_counter_ns()}\n")
|
|
267
|
+
writer.add_point(numpy.full((500, 500), i, dtype=numpy.int64))
|
|
268
|
+
eventfile.write(f"{time.perf_counter_ns()} {i}\n")
|
|
257
269
|
eventfile.flush()
|
|
258
270
|
print(f"write {i}", flush=True)
|
|
271
|
+
# Ensure flushes happen before the writer exits
|
|
272
|
+
time.sleep(0.02)
|
|
259
273
|
|
|
260
274
|
|
|
261
|
-
def _read_hdf5_file(
|
|
275
|
+
def _read_hdf5_file(
|
|
276
|
+
hdf5_filename,
|
|
277
|
+
dataset_name,
|
|
278
|
+
npoints,
|
|
279
|
+
event_filename,
|
|
280
|
+
start_event,
|
|
281
|
+
done_event,
|
|
282
|
+
nread,
|
|
283
|
+
):
|
|
262
284
|
i = 0
|
|
263
285
|
with open(event_filename, "a") as eventfile:
|
|
264
286
|
start_event.set()
|
|
265
287
|
while i < npoints:
|
|
266
|
-
for
|
|
267
|
-
eventfile.write(f"{time.perf_counter_ns()}\n")
|
|
288
|
+
for value in _iter_data(hdf5_filename, dataset_name, start_index=i):
|
|
289
|
+
eventfile.write(f"{time.perf_counter_ns()} {value}\n")
|
|
268
290
|
eventfile.flush()
|
|
269
291
|
print(f"read {i}", flush=True)
|
|
270
292
|
i += 1
|
|
293
|
+
nread.value = i
|
|
294
|
+
print("reader finished", flush=True)
|
|
295
|
+
done_event.set()
|
|
271
296
|
|
|
272
297
|
|
|
273
298
|
@h5py_utils.retry()
|
|
274
299
|
def _iter_data(hdf5_filename, dataset_name, start_index=0):
|
|
275
300
|
with h5py_utils.File(hdf5_filename) as h5file:
|
|
276
|
-
for
|
|
277
|
-
yield
|
|
301
|
+
for value in h5file[dataset_name][start_index:, 0, 0]:
|
|
302
|
+
yield int(value)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _load_events(filename):
|
|
306
|
+
with open(filename, "r") as eventfile:
|
|
307
|
+
return [tuple(map(int, line.split())) for line in eventfile]
|
|
278
308
|
|
|
279
309
|
|
|
280
310
|
def test_stack_dataset_writer_multi_chunk_dim0(tmp_path):
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import stat
|
|
3
|
+
|
|
4
|
+
import h5py
|
|
5
|
+
import numpy
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from ...data.hdf5.overwrite import ensure_items_do_not_exist
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_ensure_items_do_not_exist_nested(tmp_path):
|
|
12
|
+
filename = tmp_path / "test.h5"
|
|
13
|
+
with h5py.File(filename, "w") as f:
|
|
14
|
+
f["1.1/fit/data"] = numpy.zeros((1000, 1000))
|
|
15
|
+
f.create_group("1.1/norm")
|
|
16
|
+
size_before = os.path.getsize(filename)
|
|
17
|
+
|
|
18
|
+
ensure_items_do_not_exist(
|
|
19
|
+
f"{filename}::/1.1/fit", f"{filename}::/1.1/missing", delete_existing=True
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
with h5py.File(filename, "r") as f:
|
|
23
|
+
assert set(f["1.1"]) == {"norm"}
|
|
24
|
+
assert os.path.getsize(filename) < size_before / 10
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_ensure_items_do_not_exist_some_top_level(tmp_path):
|
|
28
|
+
filename = tmp_path / "test.h5"
|
|
29
|
+
with h5py.File(filename, "w") as f:
|
|
30
|
+
f.attrs["creator"] = "test"
|
|
31
|
+
f["1.1/data"] = numpy.zeros((1000, 1000))
|
|
32
|
+
f["2.1/data"] = numpy.zeros(10)
|
|
33
|
+
f["2.1/hard"] = f["2.1/data"]
|
|
34
|
+
f["3.1/hard"] = f["2.1/data"]
|
|
35
|
+
f["3.1/soft"] = h5py.SoftLink("/2.1/data")
|
|
36
|
+
f["3.1/external"] = h5py.ExternalLink("other.h5", "/data")
|
|
37
|
+
os.chmod(filename, 0o644)
|
|
38
|
+
size_before = os.path.getsize(filename)
|
|
39
|
+
|
|
40
|
+
ensure_items_do_not_exist(f"{filename}::/1.1", delete_existing=True)
|
|
41
|
+
|
|
42
|
+
with h5py.File(filename, "r") as f:
|
|
43
|
+
assert set(f) == {"2.1", "3.1"}
|
|
44
|
+
assert f.attrs["creator"] == "test"
|
|
45
|
+
assert f["2.1/hard"].id == f["2.1/data"].id
|
|
46
|
+
assert f["3.1/hard"].id == f["2.1/data"].id
|
|
47
|
+
assert f.get("3.1/soft", getlink=True).path == "/2.1/data"
|
|
48
|
+
assert f.get("3.1/external", getlink=True).filename == "other.h5"
|
|
49
|
+
assert os.path.getsize(filename) < size_before / 10
|
|
50
|
+
assert stat.S_IMODE(os.stat(filename).st_mode) == 0o644
|
|
51
|
+
assert os.listdir(tmp_path) == ["test.h5"]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_ensure_items_do_not_exist_all_top_level(tmp_path):
|
|
55
|
+
filename = tmp_path / "test.h5"
|
|
56
|
+
with h5py.File(filename, "w") as f:
|
|
57
|
+
f.create_group("1.1")
|
|
58
|
+
f.create_group("2.1")
|
|
59
|
+
|
|
60
|
+
ensure_items_do_not_exist(
|
|
61
|
+
f"{filename}::/1.1", f"{filename}::/2.1", delete_existing=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assert not filename.exists()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_ensure_items_do_not_exist_missing(tmp_path):
|
|
68
|
+
filename = tmp_path / "test.h5"
|
|
69
|
+
ensure_items_do_not_exist(f"{filename}::/1.1")
|
|
70
|
+
ensure_items_do_not_exist(f"{filename}::/1.1", delete_existing=True)
|
|
71
|
+
assert not filename.exists()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_ensure_items_do_not_exist_root(tmp_path):
|
|
75
|
+
filename = tmp_path / "test.h5"
|
|
76
|
+
with h5py.File(filename, "w") as f:
|
|
77
|
+
f.create_group("1.1")
|
|
78
|
+
|
|
79
|
+
with pytest.raises(FileExistsError):
|
|
80
|
+
ensure_items_do_not_exist(f"{filename}::/")
|
|
81
|
+
assert filename.exists()
|
|
82
|
+
|
|
83
|
+
ensure_items_do_not_exist(
|
|
84
|
+
f"{filename}::/1.1", f"{filename}::/", delete_existing=True
|
|
85
|
+
)
|
|
86
|
+
assert not filename.exists()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_ensure_items_do_not_exist_raise(tmp_path):
|
|
90
|
+
filename = tmp_path / "test.h5"
|
|
91
|
+
with h5py.File(filename, "w") as f:
|
|
92
|
+
f["1.1/data"] = numpy.zeros(10)
|
|
93
|
+
f.create_group("2.1")
|
|
94
|
+
|
|
95
|
+
ensure_items_do_not_exist(f"{filename}::/1.1/missing")
|
|
96
|
+
with pytest.raises(FileExistsError):
|
|
97
|
+
ensure_items_do_not_exist(f"{filename}::/1.1/missing", f"{filename}::/2.1")
|
|
98
|
+
|
|
99
|
+
with h5py.File(filename, "r") as f:
|
|
100
|
+
assert set(f) == {"1.1", "2.1"}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_ensure_items_do_not_exist_datasets_and_links(tmp_path):
|
|
104
|
+
filename = tmp_path / "test.h5"
|
|
105
|
+
with h5py.File(filename, "w") as f:
|
|
106
|
+
f["1.1/data"] = numpy.zeros(10)
|
|
107
|
+
f["1.1/other"] = numpy.zeros(10)
|
|
108
|
+
f["1.1/dangling"] = h5py.SoftLink("/missing")
|
|
109
|
+
|
|
110
|
+
ensure_items_do_not_exist(
|
|
111
|
+
f"{filename}::/1.1/data", f"{filename}::/1.1/dangling", delete_existing=True
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
with h5py.File(filename, "r") as f:
|
|
115
|
+
assert set(f["1.1"]) == {"other"}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_ensure_items_do_not_exist_external(tmp_path):
|
|
119
|
+
filename = tmp_path / "test.h5"
|
|
120
|
+
external_filename = tmp_path / "external.h5"
|
|
121
|
+
with h5py.File(external_filename, "w") as f:
|
|
122
|
+
f["group/data"] = numpy.zeros(10)
|
|
123
|
+
with h5py.File(filename, "w") as f:
|
|
124
|
+
f["1.1/data"] = numpy.zeros(10)
|
|
125
|
+
f["1.1/external"] = h5py.ExternalLink(str(external_filename), "/group")
|
|
126
|
+
|
|
127
|
+
with pytest.raises(ValueError):
|
|
128
|
+
ensure_items_do_not_exist(
|
|
129
|
+
f"{filename}::/1.1/data",
|
|
130
|
+
f"{filename}::/1.1/external/data",
|
|
131
|
+
delete_existing=True,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
with h5py.File(external_filename, "r") as f:
|
|
135
|
+
assert "group/data" in f
|
|
136
|
+
with h5py.File(filename, "r") as f:
|
|
137
|
+
assert "1.1/data" in f
|
|
138
|
+
|
|
139
|
+
ensure_items_do_not_exist(f"{filename}::/1.1/external", delete_existing=True)
|
|
140
|
+
with h5py.File(filename, "r") as f:
|
|
141
|
+
assert set(f["1.1"]) == {"data"}
|
|
142
|
+
with h5py.File(external_filename, "r") as f:
|
|
143
|
+
assert "group/data" in f
|
|
@@ -21,6 +21,7 @@ src/ewoksdata/data/blissdata/blissdatav2.py
|
|
|
21
21
|
src/ewoksdata/data/hdf5/__init__.py
|
|
22
22
|
src/ewoksdata/data/hdf5/config.py
|
|
23
23
|
src/ewoksdata/data/hdf5/mirror.py
|
|
24
|
+
src/ewoksdata/data/hdf5/overwrite.py
|
|
24
25
|
src/ewoksdata/data/hdf5/types.py
|
|
25
26
|
src/ewoksdata/data/hdf5/dataset_writer/__init__.py
|
|
26
27
|
src/ewoksdata/data/hdf5/dataset_writer/_base.py
|
|
@@ -41,5 +42,6 @@ src/ewoksdata/tests/unit/test_data_nexus.py
|
|
|
41
42
|
src/ewoksdata/tests/unit/test_dataset_writer.py
|
|
42
43
|
src/ewoksdata/tests/unit/test_find_lima_scans.py
|
|
43
44
|
src/ewoksdata/tests/unit/test_mirror_hdf5.py
|
|
45
|
+
src/ewoksdata/tests/unit/test_overwrite_hdf5.py
|
|
44
46
|
src/ewoksdata/tests/unit/data/__init__.py
|
|
45
47
|
src/ewoksdata/tests/unit/data/bliss_scans.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_non_sequential.py
RENAMED
|
File without changes
|
|
File without changes
|
{ewoksdata-2.3.0rc3 → ewoksdata-2.3.0rc4}/src/ewoksdata/data/hdf5/dataset_writer/_sequential.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|