ewoksdata 1.2.0__tar.gz → 1.3.0__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-1.2.0/src/ewoksdata.egg-info → ewoksdata-1.3.0}/PKG-INFO +2 -1
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/pyproject.toml +2 -1
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/bliss.py +7 -1
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/blissdata/blissdatav2.py +25 -9
- ewoksdata-1.3.0/src/ewoksdata/data/nexus.py +182 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/url.py +8 -3
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/test_data_nexus.py +17 -3
- {ewoksdata-1.2.0 → ewoksdata-1.3.0/src/ewoksdata.egg-info}/PKG-INFO +2 -1
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata.egg-info/requires.txt +1 -0
- ewoksdata-1.2.0/src/ewoksdata/data/nexus.py +0 -111
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/LICENSE.md +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/README.md +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/setup.cfg +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/blissdata/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/blissdata/blissdatav0.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/blissdata/blissdatav1.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/contextiterator.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/hdf5/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/hdf5/config.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/hdf5/dataset_writer.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/hdf5/types.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/data/utils.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/conftest.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/data/__init__.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/data/bliss_scans.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/test_contextiterator.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/test_data_bliss.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/test_data_hdf5.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata/tests/test_dataset_writer.py +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata.egg-info/SOURCES.txt +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata.egg-info/dependency_links.txt +0 -0
- {ewoksdata-1.2.0 → ewoksdata-1.3.0}/src/ewoksdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Author-email: ESRF <dau-pydev@esrf.fr>
|
|
6
6
|
License: # MIT License
|
|
@@ -43,6 +43,7 @@ Requires-Dist: blissdata>=0.3.4
|
|
|
43
43
|
Requires-Dist: redis
|
|
44
44
|
Requires-Dist: redis!=5.3.0b1,!=5.3.0b3
|
|
45
45
|
Requires-Dist: redis-om!=1.0.0b0; python_version < "3.9"
|
|
46
|
+
Requires-Dist: redis-om!=1.0.3b0; python_version < "3.9"
|
|
46
47
|
Provides-Extra: online
|
|
47
48
|
Requires-Dist: blissdata[tango]; extra == "online"
|
|
48
49
|
Provides-Extra: test
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ewoksdata"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0"
|
|
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" }
|
|
@@ -24,6 +24,7 @@ dependencies = [
|
|
|
24
24
|
"redis", # missing dependency in blissdata<1
|
|
25
25
|
"redis !=5.3.0b1,!=5.3.0b3",
|
|
26
26
|
"redis-om !=1.0.0b0;python_version < '3.9'", # https://github.com/redis/redis-om-python/issues/693
|
|
27
|
+
"redis-om !=1.0.3b0;python_version < '3.9'", # https://github.com/redis/redis-om-python/issues/706
|
|
27
28
|
]
|
|
28
29
|
|
|
29
30
|
[project.urls]
|
|
@@ -139,4 +139,10 @@ def iter_bliss_data(
|
|
|
139
139
|
|
|
140
140
|
|
|
141
141
|
def _is_bliss_file(h5item: Union[h5py.Dataset, h5py.Group]) -> bool:
|
|
142
|
-
return
|
|
142
|
+
return (
|
|
143
|
+
h5item.file.attrs.get("creator", "").lower() in _BLISS_PUBLISHERS
|
|
144
|
+
or h5item.file.attrs.get("publisher", "").lower() in _BLISS_PUBLISHERS
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
_BLISS_PUBLISHERS = ("bliss", "blissdata")
|
|
@@ -51,15 +51,31 @@ def iter_bliss_scan_data_from_memory(
|
|
|
51
51
|
except EndOfStream:
|
|
52
52
|
break
|
|
53
53
|
|
|
54
|
-
for stream, view in views.items()
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
views = {ctr_names[stream.name]: view for stream, view in views.items()}
|
|
55
|
+
|
|
56
|
+
nyield = min(
|
|
57
|
+
len(buffer) + len(views.get(ctr_name, ()))
|
|
58
|
+
for ctr_name, buffer in buffers.items()
|
|
59
|
+
)
|
|
60
|
+
view_positions = {ctr_name: 0 for ctr_name in buffers.keys()}
|
|
61
|
+
for _ in range(nyield):
|
|
62
|
+
data = {}
|
|
63
|
+
for ctr_name, buffer in buffers.items():
|
|
64
|
+
if buffer:
|
|
65
|
+
data_point = buffer.pop(0)
|
|
66
|
+
else:
|
|
67
|
+
view = views[ctr_name]
|
|
68
|
+
start = view_positions[ctr_name]
|
|
69
|
+
data_points = view.get_data(start=start, stop=start + 1)
|
|
70
|
+
data_point = data_points[0]
|
|
71
|
+
view_positions[ctr_name] += 1
|
|
72
|
+
data[ctr_name] = data_point
|
|
73
|
+
yield data
|
|
74
|
+
|
|
75
|
+
for ctr_name, view in views.items():
|
|
76
|
+
start = view_positions[ctr_name]
|
|
77
|
+
data_points = view.get_data(start=start, stop=None)
|
|
78
|
+
buffers[ctr_name].extend(data_points)
|
|
63
79
|
|
|
64
80
|
|
|
65
81
|
def last_lima_image(scan_key: str, lima_name: str) -> ArrayLike:
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from typing import Union, Iterator, Optional, Tuple, Sequence
|
|
4
|
+
|
|
5
|
+
import h5py
|
|
6
|
+
from silx.io import h5py_utils
|
|
7
|
+
from .url import DataUrl
|
|
8
|
+
from .url import as_dataurl
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ensure_nxclass(group: h5py.Group) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Ensure the HDF5 group has an appropriate ``NX_class`` attribute.
|
|
14
|
+
|
|
15
|
+
The attribute is assigned based on the group's depth in the hierarchy:
|
|
16
|
+
* Root group (``/``): ``NXroot``
|
|
17
|
+
* First-level group: ``NXentry``
|
|
18
|
+
* Deeper groups: ``NXcollection``
|
|
19
|
+
|
|
20
|
+
:param group: The HDF5 group to update.
|
|
21
|
+
:type group: h5py.Group
|
|
22
|
+
"""
|
|
23
|
+
if group.attrs.get("NX_class"):
|
|
24
|
+
return
|
|
25
|
+
groups = [s for s in group.name.split("/") if s]
|
|
26
|
+
n = len(groups)
|
|
27
|
+
if n == 0:
|
|
28
|
+
group.attrs["NX_class"] = "NXroot"
|
|
29
|
+
elif n == 1:
|
|
30
|
+
group.attrs["NX_class"] = "NXentry"
|
|
31
|
+
else:
|
|
32
|
+
group.attrs["NX_class"] = "NXcollection"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def select_default_plot(nxdata: h5py.Group) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Set the default NXdata group for visualization in the parent hierarchy.
|
|
38
|
+
|
|
39
|
+
The function walks up the group tree and sets the ``default`` attribute
|
|
40
|
+
in each parent to point to the given NXdata group.
|
|
41
|
+
|
|
42
|
+
:param nxdata: An HDF5 group representing an NXdata node.
|
|
43
|
+
:type nxdata: h5py.Group
|
|
44
|
+
"""
|
|
45
|
+
parent = nxdata.parent
|
|
46
|
+
for name in nxdata.name.split("/")[::-1]:
|
|
47
|
+
if not name:
|
|
48
|
+
continue
|
|
49
|
+
parent.attrs["default"] = name
|
|
50
|
+
parent = parent.parent
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_url(url: str, overwrite: bool = False, **open_options) -> DataUrl:
|
|
54
|
+
"""
|
|
55
|
+
Create (or ensure the existence of) an HDF5 group specified by a URL.
|
|
56
|
+
|
|
57
|
+
:param url: The HDF5 URL (e.g. ``path/to/file.h5::/entry/data``).
|
|
58
|
+
:param overwrite: If True, overwrite the final group if it exists.
|
|
59
|
+
If False, reuse existing groups. Default is False.
|
|
60
|
+
:param open_options: Additional options passed to ``h5py.File``.
|
|
61
|
+
:return: A DataUrl object pointing to the created or existing group.
|
|
62
|
+
"""
|
|
63
|
+
url = as_dataurl(url)
|
|
64
|
+
filename = url.file_path()
|
|
65
|
+
data_path = url.data_path() or "/"
|
|
66
|
+
dirname = os.path.dirname(filename)
|
|
67
|
+
if dirname:
|
|
68
|
+
os.makedirs(dirname, exist_ok=True)
|
|
69
|
+
_ = open_options.setdefault("mode", "a")
|
|
70
|
+
with h5py_utils.open_item(filename, "/", **open_options) as parent:
|
|
71
|
+
h5item, _ = _create_h5group(parent, data_path, overwrite=overwrite)
|
|
72
|
+
return as_dataurl(f"{filename}::{h5item.name}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_nxentry(h5item: Union[h5py.Dataset, h5py.Group]) -> h5py.Group:
|
|
76
|
+
"""
|
|
77
|
+
Return the top-level NXentry group containing the given HDF5 item.
|
|
78
|
+
|
|
79
|
+
:param h5item: An HDF5 dataset or group.
|
|
80
|
+
:return: The NXentry group containing ``h5item``.
|
|
81
|
+
:raises ValueError: If the item does not belong to an NXentry hierarchy.
|
|
82
|
+
"""
|
|
83
|
+
parts = [s for s in h5item.name.split("/") if s]
|
|
84
|
+
if parts:
|
|
85
|
+
return h5item.file[parts[0]]
|
|
86
|
+
raise ValueError("HDF5 item must be part of an NXentry")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@contextmanager
|
|
90
|
+
def create_nexus_group(
|
|
91
|
+
url: Union[str, DataUrl],
|
|
92
|
+
retry_timeout=None,
|
|
93
|
+
retry_period=None,
|
|
94
|
+
default_levels: Optional[Sequence[str]] = None,
|
|
95
|
+
**open_options,
|
|
96
|
+
) -> Iterator[Tuple[h5py.Group, bool]]:
|
|
97
|
+
"""
|
|
98
|
+
Context manager to create or access an HDF5 group specified by a URL.
|
|
99
|
+
|
|
100
|
+
The data path inside HDF5 is ensured to have at least ``default_levels`` levels.
|
|
101
|
+
|
|
102
|
+
:param url: The HDF5 URL (e.g. ``path/to/file.h5::/entry/data``).
|
|
103
|
+
:param retry_timeout: Maximum time in seconds to retry opening the file.
|
|
104
|
+
:param retry_period: Time in seconds between retries.
|
|
105
|
+
:param default_levels: Minimum path hierarchy touse when missing. Default is ``["/", "results"]``.
|
|
106
|
+
:param open_options: Additional options passed to ``h5py.File``.
|
|
107
|
+
:yields: Tuple of (HDF5 group, already_existed flag).
|
|
108
|
+
"""
|
|
109
|
+
url = as_dataurl(url)
|
|
110
|
+
filename = url.file_path()
|
|
111
|
+
itemname = url.data_path() or "/"
|
|
112
|
+
dirname = os.path.dirname(filename)
|
|
113
|
+
if dirname:
|
|
114
|
+
os.makedirs(dirname, exist_ok=True)
|
|
115
|
+
open_options.setdefault("mode", "a")
|
|
116
|
+
with h5py_utils.open_item(
|
|
117
|
+
filename,
|
|
118
|
+
"/",
|
|
119
|
+
retry_timeout=retry_timeout,
|
|
120
|
+
retry_period=retry_period,
|
|
121
|
+
**open_options,
|
|
122
|
+
) as root:
|
|
123
|
+
yield _create_h5group(root, itemname, default_levels=default_levels)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _create_h5group(
|
|
127
|
+
parent: h5py.Group,
|
|
128
|
+
data_path: str,
|
|
129
|
+
default_levels: Optional[Sequence[str]] = None,
|
|
130
|
+
overwrite: bool = False,
|
|
131
|
+
) -> Tuple[h5py.Group, bool]:
|
|
132
|
+
"""
|
|
133
|
+
Create or retrieve an HDF5 group under a given parent.
|
|
134
|
+
|
|
135
|
+
Ensures that intermediate groups exist, optionally enforcing a minimum
|
|
136
|
+
depth using ``default_levels``.
|
|
137
|
+
|
|
138
|
+
Missing ``NX_class`` attributes are automatically created on all levels.
|
|
139
|
+
The final group can be replaced if ``overwrite=True``.
|
|
140
|
+
|
|
141
|
+
:param parent: Parent group under which the path should be created.
|
|
142
|
+
:param data_path: Group path to create relative to ``parent``.
|
|
143
|
+
:param default_levels: Minimum hierarchy to enforce.
|
|
144
|
+
If ``data_path`` is shorter, extra levels are added.
|
|
145
|
+
:param overwrite: If True, delete and recreate the final group if it exists.
|
|
146
|
+
Default is False.
|
|
147
|
+
:return: Tuple of (HDF5 group, already_existed flag).
|
|
148
|
+
"""
|
|
149
|
+
# Ensure the data path has at least N levels as defined by ``default_levels``
|
|
150
|
+
groups = [s for s in data_path.split("/") if s]
|
|
151
|
+
if default_levels:
|
|
152
|
+
default_levels = list(default_levels)
|
|
153
|
+
else:
|
|
154
|
+
default_levels = ["results"]
|
|
155
|
+
if len(groups) < len(default_levels):
|
|
156
|
+
groups += default_levels[len(groups) :]
|
|
157
|
+
|
|
158
|
+
# Ensure parent groups exist + ensure NX_class
|
|
159
|
+
create = False
|
|
160
|
+
ensure_nxclass(parent)
|
|
161
|
+
for group in groups[:-1]:
|
|
162
|
+
if group in parent:
|
|
163
|
+
parent = parent[group]
|
|
164
|
+
else:
|
|
165
|
+
parent = parent.create_group(group)
|
|
166
|
+
create = True
|
|
167
|
+
ensure_nxclass(parent)
|
|
168
|
+
|
|
169
|
+
# Final group: create, overwrite or confirm existence + ensure NX_class
|
|
170
|
+
group = groups[-1]
|
|
171
|
+
if group in parent:
|
|
172
|
+
if overwrite:
|
|
173
|
+
del parent[group]
|
|
174
|
+
parent = parent.create_group(group)
|
|
175
|
+
else:
|
|
176
|
+
parent = parent[group]
|
|
177
|
+
else:
|
|
178
|
+
parent = parent.create_group(group)
|
|
179
|
+
create = True
|
|
180
|
+
ensure_nxclass(parent)
|
|
181
|
+
|
|
182
|
+
return parent, not create
|
|
@@ -11,12 +11,17 @@ def as_dataurl(url: Union[str, DataUrl]) -> DataUrl:
|
|
|
11
11
|
return url
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def as_h5url(url: Union[str, DataUrl]) -> DataUrl:
|
|
15
15
|
url = as_dataurl(url)
|
|
16
|
+
if not url.data_path():
|
|
17
|
+
return DataUrl(f"{url.file_path()}::/")
|
|
18
|
+
return url
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def h5dataset_url_parse(url: Union[str, DataUrl]) -> Tuple[str, str, Tuple]:
|
|
22
|
+
url = as_h5url(url)
|
|
16
23
|
filename = str(url.file_path())
|
|
17
24
|
h5path = url.data_path()
|
|
18
|
-
if h5path is None:
|
|
19
|
-
h5path = "/"
|
|
20
25
|
idx = url.data_slice()
|
|
21
26
|
if idx is None:
|
|
22
27
|
idx = tuple()
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import time
|
|
2
2
|
|
|
3
|
+
import pytest
|
|
3
4
|
from silx.io import h5py_utils
|
|
4
5
|
from silx.io.url import DataUrl
|
|
5
6
|
|
|
7
|
+
from ..data import url
|
|
6
8
|
from ..data import nexus
|
|
7
9
|
|
|
8
10
|
|
|
@@ -38,11 +40,23 @@ def test_create_nexus_group(tmp_path):
|
|
|
38
40
|
assert h5item.name == "/c/b"
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
@pytest.mark.parametrize("as_string", [True, False])
|
|
44
|
+
@pytest.mark.parametrize("data_path", ["", "::/1.1/measurement/integrated"])
|
|
45
|
+
def test_create_url(tmp_path, data_path, as_string):
|
|
46
|
+
tmp_url = DataUrl(f"{tmp_path / 'file.h5'}{data_path}")
|
|
47
|
+
|
|
48
|
+
if data_path:
|
|
49
|
+
expected_url = url.as_dataurl(tmp_url.path())
|
|
50
|
+
else:
|
|
51
|
+
expected_url = url.as_dataurl(f"{tmp_url.path()}::/results")
|
|
52
|
+
|
|
53
|
+
if as_string:
|
|
54
|
+
input_url = tmp_url.path()
|
|
55
|
+
else:
|
|
56
|
+
input_url = tmp_url
|
|
43
57
|
|
|
44
58
|
created_url = nexus.create_url(input_url)
|
|
45
|
-
assert created_url ==
|
|
59
|
+
assert created_url == expected_url
|
|
46
60
|
|
|
47
61
|
with h5py_utils.open_item(
|
|
48
62
|
created_url.file_path(), created_url.data_path(), mode="a"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Author-email: ESRF <dau-pydev@esrf.fr>
|
|
6
6
|
License: # MIT License
|
|
@@ -43,6 +43,7 @@ Requires-Dist: blissdata>=0.3.4
|
|
|
43
43
|
Requires-Dist: redis
|
|
44
44
|
Requires-Dist: redis!=5.3.0b1,!=5.3.0b3
|
|
45
45
|
Requires-Dist: redis-om!=1.0.0b0; python_version < "3.9"
|
|
46
|
+
Requires-Dist: redis-om!=1.0.3b0; python_version < "3.9"
|
|
46
47
|
Provides-Extra: online
|
|
47
48
|
Requires-Dist: blissdata[tango]; extra == "online"
|
|
48
49
|
Provides-Extra: test
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from typing import Union, Iterator, Optional, Tuple
|
|
4
|
-
|
|
5
|
-
import h5py
|
|
6
|
-
from silx.io import h5py_utils
|
|
7
|
-
from .url import DataUrl
|
|
8
|
-
from .url import as_dataurl
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def ensure_nxclass(group: h5py.Group) -> None:
|
|
12
|
-
if group.attrs.get("NX_class"):
|
|
13
|
-
return
|
|
14
|
-
groups = [s for s in group.name.split("/") if s]
|
|
15
|
-
n = len(groups)
|
|
16
|
-
if n == 0:
|
|
17
|
-
group.attrs["NX_class"] = "NXroot"
|
|
18
|
-
elif n == 1:
|
|
19
|
-
group.attrs["NX_class"] = "NXentry"
|
|
20
|
-
else:
|
|
21
|
-
group.attrs["NX_class"] = "NXcollection"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def select_default_plot(nxdata: h5py.Group) -> None:
|
|
25
|
-
parent = nxdata.parent
|
|
26
|
-
for name in nxdata.name.split("/")[::-1]:
|
|
27
|
-
if not name:
|
|
28
|
-
continue
|
|
29
|
-
parent.attrs["default"] = name
|
|
30
|
-
parent = parent.parent
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def create_url(url: str, overwrite: bool = False, **open_options) -> DataUrl:
|
|
34
|
-
url = as_dataurl(url)
|
|
35
|
-
filename = url.file_path()
|
|
36
|
-
dirname = os.path.dirname(filename)
|
|
37
|
-
if dirname:
|
|
38
|
-
os.makedirs(dirname, exist_ok=True)
|
|
39
|
-
open_options.setdefault("mode", "a")
|
|
40
|
-
with h5py_utils.open_item(filename, "/", **open_options) as parent:
|
|
41
|
-
if overwrite and url.data_path() in parent:
|
|
42
|
-
del parent[url.data_path()]
|
|
43
|
-
h5item, _ = _create_h5group(parent, url.data_path())
|
|
44
|
-
return as_dataurl(f"{filename}::{h5item.name}")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_nxentry(h5item: Union[h5py.Dataset, h5py.Group]):
|
|
48
|
-
parts = [s for s in h5item.name.split("/") if s]
|
|
49
|
-
if parts:
|
|
50
|
-
return h5item.file[parts[0]]
|
|
51
|
-
raise ValueError("HDF5 item must be part of an NXentry")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@contextmanager
|
|
55
|
-
def create_nexus_group(
|
|
56
|
-
url: Union[str, DataUrl],
|
|
57
|
-
retry_timeout=None,
|
|
58
|
-
retry_period=None,
|
|
59
|
-
default_levels: Optional[Tuple[str]] = None,
|
|
60
|
-
**open_options,
|
|
61
|
-
) -> Iterator[Tuple[h5py.Group, bool]]:
|
|
62
|
-
"""
|
|
63
|
-
:yields: (h5group, already_existed)
|
|
64
|
-
"""
|
|
65
|
-
url = as_dataurl(url)
|
|
66
|
-
filename = url.file_path()
|
|
67
|
-
itemname = url.data_path()
|
|
68
|
-
if not itemname:
|
|
69
|
-
itemname = "/"
|
|
70
|
-
dirname = os.path.dirname(filename)
|
|
71
|
-
if dirname:
|
|
72
|
-
os.makedirs(dirname, exist_ok=True)
|
|
73
|
-
open_options.setdefault("mode", "a")
|
|
74
|
-
with h5py_utils.open_item(
|
|
75
|
-
filename,
|
|
76
|
-
"/",
|
|
77
|
-
retry_timeout=retry_timeout,
|
|
78
|
-
retry_period=retry_period,
|
|
79
|
-
**open_options,
|
|
80
|
-
) as root:
|
|
81
|
-
yield _create_h5group(root, itemname, default_levels=default_levels)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def _create_h5group(
|
|
85
|
-
parent, data_path: Optional[str], default_levels: Optional[Tuple[str]] = None
|
|
86
|
-
) -> Tuple[h5py.Group, bool]:
|
|
87
|
-
"""
|
|
88
|
-
:yields: (h5group, already_existed)
|
|
89
|
-
"""
|
|
90
|
-
if not data_path:
|
|
91
|
-
data_path = ""
|
|
92
|
-
groups = [""] + [s for s in data_path.split("/") if s]
|
|
93
|
-
if default_levels:
|
|
94
|
-
default_levels = [""] + list(default_levels)
|
|
95
|
-
else:
|
|
96
|
-
default_levels = ["", "results"]
|
|
97
|
-
if len(groups) < len(default_levels):
|
|
98
|
-
groups += default_levels[len(groups) :]
|
|
99
|
-
data_path = "/".join(groups)
|
|
100
|
-
groups[0] = "/"
|
|
101
|
-
|
|
102
|
-
create = False
|
|
103
|
-
ensure_nxclass(parent)
|
|
104
|
-
for group in groups:
|
|
105
|
-
if group in parent:
|
|
106
|
-
parent = parent[group]
|
|
107
|
-
else:
|
|
108
|
-
parent = parent.create_group(group)
|
|
109
|
-
create = True
|
|
110
|
-
ensure_nxclass(parent)
|
|
111
|
-
return parent, not create
|
|
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
|
|
File without changes
|
|
File without changes
|