plato-hdf5 2024.1.3__py3-none-any.whl

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.
@@ -0,0 +1,154 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ import h5py
6
+ import natsort
7
+ import numpy as np
8
+ from egse.spw import DataDataPacket
9
+ from egse.spw import HousekeepingPacket
10
+ from egse.spw import OverscanDataPacket
11
+ from egse.spw import TimecodePacket
12
+ from egse.persistence import PersistenceLayer # -> circular dependency because this import loads this module as a plugin
13
+
14
+ LOGGER = logging.getLogger(__name__)
15
+
16
+
17
+ class HDF5(PersistenceLayer):
18
+ extension = "hdf5"
19
+
20
+ def __init__(self, filename, prep: dict = None):
21
+ """
22
+ The `prep` argument needs at least the following mandatory key:value pairs:
23
+
24
+ * mode: the mode used for opening the file [default is 'r']
25
+
26
+ """
27
+ # LOGGER.debug(f"{h5py.version.hdf5_version=}")
28
+ self._filepath = Path(filename)
29
+ self._mode = prep.get("mode") or "r"
30
+ self._h5file: Optional[h5py.File] = None
31
+
32
+ def __enter__(self):
33
+ self.open(mode=self._mode)
34
+ return self
35
+
36
+ def __exit__(self, exc_type, exc_val, exc_tb):
37
+ self.close()
38
+
39
+ def open(self, mode=None):
40
+ self._mode = mode or self._mode
41
+ LOGGER.debug(f"Opening file {self._filepath} in mode '{self._mode}'")
42
+ self._h5file = h5py.File(self._filepath, mode=self._mode)
43
+
44
+ # File "h5py/h5f.pyx", line 554, in h5py.h5f.FileID.start_swmr_write
45
+ # RuntimeError: Unable to start swmr writing (file superblock version - should be at least 3)
46
+ # self._h5file.swmr_mode = True
47
+
48
+ def close(self):
49
+ self._h5file.close()
50
+
51
+ def exists(self):
52
+ return self._filepath.exists()
53
+
54
+ def create(self, data):
55
+ """
56
+ Store the given data in the HDF5 file. The data argument shall be a dictionary where the
57
+ keys represent the group where the data shall be saved, and the value is the data to be
58
+ saved. When the key ends with ":ATTRS", then the value is a list of attributes to that
59
+ group. Values can be of different type and are processed if needed.
60
+
61
+ An example data argument:
62
+
63
+ {
64
+ "/10/timecode": tc_packet,
65
+ "/10/timecode:ATTRS": [("timestamp", timestamp)],
66
+ "/10/command/": f"{command.__name__}, {args=}",
67
+ "/10/register/": self.register_map.get_memory_map_as_ndarray()
68
+ }
69
+
70
+ The example saves a Timecode packet in the group "/10/timecode" and attaches a timestamp
71
+ as an attribute called "timestamp" to the same group. It then adds a command string in
72
+ the "/10/command" group and finally adds a register memory map (an np.ndarray) in the group
73
+ "/10/register".
74
+
75
+ Args:
76
+ data (dict): a dictionary containing the data that needs to be saved.
77
+
78
+ Returns:
79
+ None.
80
+ """
81
+ for key, value in data.items():
82
+ if key.endswith(":ATTRS"):
83
+ a_key = key.split(":")[0]
84
+ for k, v in value:
85
+ self._h5file[a_key].attrs[k] = v
86
+ if isinstance(value, TimecodePacket):
87
+ self._h5file[key] = value.timecode
88
+ if isinstance(value, HousekeepingPacket):
89
+ self._h5file[key] = value.packet_as_ndarray
90
+ if isinstance(value, HousekeepingData):
91
+ self._h5file[key] = value.data_as_ndarray
92
+ if isinstance(value, DataDataPacket):
93
+ self._h5file[key] = value.packet_as_ndarray
94
+ if isinstance(value, OverscanDataPacket):
95
+ self._h5file[key] = value.packet_as_ndarray
96
+ if isinstance(value, (str, bytearray, np.ndarray)):
97
+
98
+ # if we save a command, put it into a 'commands' group.
99
+ # This is a special case that is the result of issue #1461
100
+
101
+ if 'command' in key:
102
+ idx = key.split('/')[1]
103
+ if idx in self._h5file and 'commands' in self._h5file[idx]:
104
+ last_idx = int(sorted(self._h5file[f"/{idx}/commands"].keys())[-1])
105
+ key = f"/{idx}/commands/{last_idx + 1}"
106
+ else:
107
+ key = f"/{idx}/commands/0"
108
+
109
+ self._h5file[key] = value
110
+
111
+ def read(self, select=None):
112
+ """
113
+ Read information or data from the HDF5 file.
114
+
115
+ The `select` argument can contain the following information:
116
+
117
+ * the string 'number_of_groups': request to determine the number of top groups in
118
+ the HDF5 file.
119
+ * the string 'last_top_group': request the name/key of the last item in the top group.
120
+ The last item is the last element of the list of keys, sorted with natural order.
121
+
122
+ Args:
123
+ select (str or dict): specify which information should be read
124
+
125
+ Returns:
126
+ When 'number_of_groups', return an integer, when 'last_top_group' return a string.
127
+ """
128
+ if select == "number_of_groups":
129
+ return len(self._h5file.keys())
130
+ if select == "last_top_group":
131
+ keys = self._h5file.keys()
132
+
133
+ LOGGER.debug(f"{self._h5file.filename}: {keys=}")
134
+
135
+ return 0 if len(keys) == 0 else natsort.natsorted(keys)[-1]
136
+
137
+ # This following lines is a longer version of the previous two lines, keep them for
138
+ # debugging because I had problems and not yet sure what is the cause...
139
+
140
+ # sorted_keys = natsort.natsorted(keys)
141
+ # LOGGER.debug(f"{self._h5file.filename}: {sorted_keys=}")
142
+ # key = sorted_keys[-1]
143
+ # LOGGER.debug(f"{key=}")
144
+ # return key
145
+
146
+ def update(self, idx, data):
147
+
148
+ pass
149
+
150
+ def delete(self, idx):
151
+ pass
152
+
153
+ def get_filepath(self):
154
+ return self._filepath
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: plato-hdf5
3
+ Version: 2024.1.3
4
+ Summary: HDF5 Persistence sub-class for CGSE
5
+ Author: IVS KU Leuven
6
+ Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
7
+ License-Expression: MIT
8
+ Keywords: CGSE,Common-EGSE,hardware testing,software framework
9
+ Requires-Python: >=3.9
10
+ Requires-Dist: cgse-common
11
+ Requires-Dist: h5py
12
+ Requires-Dist: natsort
13
+ Requires-Dist: plato-spw
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest; extra == 'test'
16
+ Requires-Dist: pytest-cov; extra == 'test'
17
+ Requires-Dist: pytest-mock; extra == 'test'
@@ -0,0 +1,5 @@
1
+ egse/plugins/storage/hdf5.py,sha256=cc0CdCl67JMaM-KqsePai9SxKreqyJCeTo_DNGYJHPc,5745
2
+ plato_hdf5-2024.1.3.dist-info/METADATA,sha256=qhxC2Ef6q1tBVgCw8ymWMogCQYsXdbjDk9BTBkrFh2c,569
3
+ plato_hdf5-2024.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4
+ plato_hdf5-2024.1.3.dist-info/entry_points.txt,sha256=Y1-rTC795sMY-014-9SiUkoQmiLgDY-RoYVyqfbCqUE,107
5
+ plato_hdf5-2024.1.3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [cgse.storage.persistence]
2
+ HDF5 = egse.plugins.storage.hdf5:HDF5
3
+
4
+ [cgse.version]
5
+ plato-hdf5 = egse.plugins