blissdata 1.0.2__tar.gz → 1.0.4__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.
Files changed (67) hide show
  1. {blissdata-1.0.2 → blissdata-1.0.4}/PKG-INFO +3 -3
  2. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/__init__.py +1 -1
  3. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/bliss.py +2 -5
  4. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/hdf5.py +52 -24
  5. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/scan.py +7 -6
  6. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/stream.py +8 -1
  7. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/conftest.py +2 -2
  8. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/scanner.py +2 -2
  9. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_streaming.py +16 -0
  10. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/PKG-INFO +3 -3
  11. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/requires.txt +2 -2
  12. {blissdata-1.0.2 → blissdata-1.0.4}/setup.cfg +2 -2
  13. {blissdata-1.0.2 → blissdata-1.0.4}/LICENSE +0 -0
  14. {blissdata-1.0.2 → blissdata-1.0.4}/README.md +0 -0
  15. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/__init__.py +0 -0
  16. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/_base.py +0 -0
  17. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/config.py +0 -0
  18. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/data.py +0 -0
  19. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/files.py +0 -0
  20. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/__init__.py +0 -0
  21. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/abstract.py +0 -0
  22. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/dynamic_hdf5.py +0 -0
  23. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/file_arguments.py +0 -0
  24. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/static_hdf5.py +0 -0
  25. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/__init__.py +0 -0
  26. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/hdf5_retry.py +0 -0
  27. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/lima.py +0 -0
  28. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/types.py +0 -0
  29. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/__init__.py +0 -0
  30. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/client.py +0 -0
  31. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/image_utils.py +0 -0
  32. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/__init__.py +0 -0
  33. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/__init__.py +0 -0
  34. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/json.py +0 -0
  35. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/numeric.py +0 -0
  36. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/exceptions.py +0 -0
  37. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/identities.py +0 -0
  38. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/memory_tracker.py +0 -0
  39. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/sink.py +0 -0
  40. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/store.py +0 -0
  41. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/scan.py +0 -0
  42. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/__init__.py +0 -0
  43. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/scan_info.py +0 -0
  44. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/scan_info_display.py +0 -0
  45. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/stream.py +0 -0
  46. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/__init__.py +0 -0
  47. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/__init__.py +0 -0
  48. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_config.py +0 -0
  49. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_data.py +0 -0
  50. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_files.py +0 -0
  51. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/__init__.py +0 -0
  52. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/test_dynamic_files.py +0 -0
  53. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/test_static_files.py +0 -0
  54. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/__init__.py +0 -0
  55. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/conftest.py +0 -0
  56. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/encoding/__init__.py +0 -0
  57. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/encoding/test_numeric.py +0 -0
  58. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_memory_tracker.py +0 -0
  59. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_scan.py +0 -0
  60. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_search.py +0 -0
  61. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/utils.py +0 -0
  62. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/SOURCES.txt +0 -0
  63. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/dependency_links.txt +0 -0
  64. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/entry_points.txt +0 -0
  65. {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/top_level.txt +0 -0
  66. {blissdata-1.0.2 → blissdata-1.0.4}/pyproject.toml +0 -0
  67. {blissdata-1.0.2 → blissdata-1.0.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blissdata
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Bliss data streaming client
5
5
  Home-page: https://gitlab.esrf.fr/bliss/bliss/-/tree/master/blissdata
6
6
  Author: BCU (ESRF)
@@ -15,8 +15,8 @@ Requires-Dist: beartype
15
15
  Requires-Dist: h5py<3.10,>=2.9
16
16
  Requires-Dist: msgpack
17
17
  Requires-Dist: numpy>=1.13
18
- Requires-Dist: redis>=4.6.0
19
- Requires-Dist: redis-om
18
+ Requires-Dist: redis<5,>=4.6.0
19
+ Requires-Dist: redis-om<0.3
20
20
  Requires-Dist: silx>=1.1.0
21
21
  Provides-Extra: tango
22
22
  Requires-Dist: pytango>=9.3.1; extra == "tango"
@@ -4,4 +4,4 @@
4
4
  #
5
5
  # Copyright (c) Beamline Control Unit, ESRF
6
6
  # Distributed under the GNU LGPLv3. See LICENSE for more info.
7
- __version__ = "1.0.2"
7
+ __version__ = "1.0.4"
@@ -102,7 +102,7 @@ class BlissDynamicHDF5Handler(hdf5.DynamicHDF5Handler):
102
102
  if h5item.name == hdf5.SEP:
103
103
  return False
104
104
  scan = [s for s in h5item.name.split(hdf5.SEP) if s][0]
105
- nxentry = self.file_obj.file[scan]
105
+ nxentry = self.file_obj[scan]
106
106
  except AttributeError:
107
107
  raise RetryError("file is closed")
108
108
  if "end_time" in nxentry:
@@ -119,10 +119,7 @@ class BlissDynamicHDF5Handler(hdf5.DynamicHDF5Handler):
119
119
  nxentry = self.file_obj[scan]
120
120
  except AttributeError:
121
121
  raise RetryError("file is closed")
122
- if "end_time" in nxentry:
123
- # Last dataset written to the file
124
- return True
125
- return self._get_writer_status(nxentry) in ("SUCCEEDED", "FAILED")
122
+ return "end_time" in nxentry # Last dataset written to the file
126
123
 
127
124
  def _get_writer_status(self, nxentry: h5py.Dataset) -> Optional[str]:
128
125
  nxnote = nxentry.get("writer", None)
@@ -146,19 +146,22 @@ class DynamicHDF5Handler:
146
146
  return item
147
147
  try:
148
148
  item = self.file_obj[name]
149
- except KeyError as e:
150
- parent_name = name.rpartition(SEP)[0]
149
+ except KeyError:
150
+ parent_name, _, item_name = name.rpartition(SEP)
151
151
  if not parent_name:
152
152
  parent_name = SEP
153
- if parent_name != name:
154
- try:
155
- parent_item = self._get_item(parent_name)
156
- except KeyError:
157
- pass
158
- else:
159
- if self._is_finished(parent_item):
160
- raise RetryTimeoutError(str(e)) from e
161
- raise
153
+ if parent_name == name:
154
+ raise
155
+ parent_item = self._get_item(parent_name)
156
+ if not self._is_finished(parent_item):
157
+ raise # item could still be created
158
+ try:
159
+ item = parent_item[item_name] # try one more time
160
+ except KeyError as e:
161
+ if not _key_does_not_exist(e):
162
+ raise
163
+ # item will never be created
164
+ raise RetryTimeoutError(str(e)) from e
162
165
  self._native_items[name] = item
163
166
  return item
164
167
 
@@ -174,19 +177,26 @@ class DynamicHDF5Handler:
174
177
  item = self._get_item(name)
175
178
  try:
176
179
  return item.attrs[key]
177
- except KeyError as e:
178
- if self._is_finished(item):
180
+ except KeyError:
181
+ if not self._is_finished(item):
182
+ raise # attribute could still be created
183
+ try:
184
+ return item.attrs[key] # try one more time
185
+ except KeyError as e:
186
+ if not _key_does_not_exist(e):
187
+ raise
188
+ # attribute will never be created
179
189
  raise RetryTimeoutError(str(e)) from e
180
- raise
181
190
 
182
191
  def _iter_attrs(self, name: str, start_index: int = 0) -> Iterator[str]:
183
192
  item = self._get_item(name)
193
+ is_complete = self._is_initialized(item)
184
194
  if start_index == 0:
185
195
  yield from item.attrs
186
196
  else:
187
197
  yield from list(item.attrs)[start_index:]
188
- if not self._is_initialized(item):
189
- raise RetryError("not fully initialized")
198
+ if not is_complete:
199
+ raise RetryError("attributes could still be added")
190
200
 
191
201
  def _len_attrs(self, name: str) -> int:
192
202
  item = self._get_item(name)
@@ -198,19 +208,19 @@ class DynamicHDF5Handler:
198
208
  item = self._get_item(name)
199
209
  self._check_dataset_before_read(item)
200
210
  if self.is_group(item):
201
- is_initialized = self._is_initialized(item)
211
+ is_complete = self._is_initialized(item)
202
212
  for key in self._iter_group(item, start_index):
203
- is_initialized = False
213
+ is_complete = False
204
214
  yield key
205
- if not is_initialized:
206
- raise RetryError("not initialized")
215
+ if not is_complete:
216
+ raise RetryError("children could still be added to the group")
207
217
  else:
208
- is_finished = self._is_finished(item)
218
+ is_complete = self._is_finished(item)
209
219
  for data in self._iter_dataset(item, start_index):
210
- is_finished = False
220
+ is_complete = False
211
221
  yield data
212
- if not is_finished:
213
- raise RetryError("not finished")
222
+ if not is_complete:
223
+ raise RetryError("dataset could still grow")
214
224
 
215
225
  @staticmethod
216
226
  def is_group(h5item: HDF5Item):
@@ -250,3 +260,21 @@ class DynamicHDF5Handler:
250
260
 
251
261
  def _is_finished(self, h5item: HDF5Item) -> bool:
252
262
  return True
263
+
264
+
265
+ def _key_does_not_exist(exc: KeyError) -> bool:
266
+ """
267
+ A real KeyError (meaning that the key does not exist) gives the following error message:
268
+
269
+ .. code
270
+
271
+ KeyError: "Unable to synchronously open object (object 'end_time' doesn't exist)"
272
+
273
+ An example of a KeyError caused by another reason
274
+
275
+ .. code
276
+
277
+ KeyError: 'Unable to synchronously open object (addr overflow, addr = 64392369, size = 96, eoa = 64365353)'
278
+
279
+ """
280
+ return "doesn't exist" in str(exc)
@@ -73,6 +73,8 @@ class Scan:
73
73
  for prop_name in scan._data_store._scan_model.id.field.type_.schema()[
74
74
  "properties"
75
75
  ].keys():
76
+ if prop_name == "pk":
77
+ continue
76
78
 
77
79
  def get_id_field(self, field=prop_name):
78
80
  return getattr(self._model.id, field, None)
@@ -109,6 +111,8 @@ class Scan:
109
111
  for prop_name in scan._data_store._scan_model.id.field.type_.schema()[
110
112
  "properties"
111
113
  ].keys():
114
+ if prop_name == "pk":
115
+ continue
112
116
 
113
117
  def get_id_field(self, field=prop_name):
114
118
  return getattr(self._model.id, field, None)
@@ -278,10 +282,7 @@ class Scan:
278
282
  the still running writers to fail.
279
283
  """
280
284
  for stream_writer in self._writer_streams.values():
281
- if not stream_writer.is_sealed():
282
- stream_writer.seal()
283
- else:
284
- stream_writer._sink.stop()
285
+ stream_writer.seal()
285
286
  self._writer_streams = {}
286
287
 
287
288
  @needs_write_permission
@@ -301,15 +302,15 @@ class Scan:
301
302
  @needs_write_permission
302
303
  def stop(self):
303
304
  if self.state is ScanState.STARTED:
304
- self._set_state(ScanState.STOPPED)
305
305
  self._close_stream_writers()
306
+ self._set_state(ScanState.STOPPED)
306
307
  else:
307
308
  raise UnauthorizeStateTransition(self.state, ScanState.STOPPED)
308
309
 
309
310
  @needs_write_permission
310
311
  def close(self):
311
- self._set_state(ScanState.CLOSED)
312
312
  self._close_stream_writers()
313
+ self._set_state(ScanState.CLOSED)
313
314
 
314
315
  @needs_write_permission
315
316
  def _set_state(self, state):
@@ -4,6 +4,7 @@
4
4
  #
5
5
  # Copyright (c) Beamline Control Unit, ESRF
6
6
  # Distributed under the GNU LGPLv3. See LICENSE for more info.
7
+ import redis
7
8
  from functools import wraps
8
9
  from dataclasses import dataclass
9
10
  from collections.abc import Mapping
@@ -270,8 +271,14 @@ class Stream:
270
271
 
271
272
  @needs_write_permission
272
273
  def seal(self):
273
- self._sink.fcall("seal_stream", 1, self.key)
274
274
  self._sink.stop()
275
+ try:
276
+ return self._data_store._redis.fcall("seal_stream", 1, self.key)
277
+ except redis.exceptions.ResponseError as e:
278
+ if "is already sealed" in str(e):
279
+ return len(self)
280
+ else:
281
+ raise
275
282
 
276
283
  def __del__(self):
277
284
  # Thread based sinks can't be garbage collected, because their internal thread
@@ -3,7 +3,7 @@ import subprocess
3
3
  from redis import Redis
4
4
  from socket import socket
5
5
  from pytest import fixture
6
- from pytest_redis import factories
6
+ from pytest_redis.factories.noproc import redis_noproc
7
7
 
8
8
  from blissdata.redis_engine.store import DataStore
9
9
 
@@ -15,7 +15,7 @@ def _find_available_port():
15
15
 
16
16
 
17
17
  redis_port = _find_available_port()
18
- redis_db = factories.redis_noproc(host="localhost", port=redis_port, startup_timeout=1)
18
+ redis_db = redis_noproc(host="localhost", port=redis_port, startup_timeout=1)
19
19
 
20
20
 
21
21
  def pytest_configure(config):
@@ -268,7 +268,7 @@ def writer_context(filename: str, scan_number: int) -> Iterator[h5py.Group]:
268
268
 
269
269
  try:
270
270
  entry.attrs["NX_class"] = "NXentry"
271
- entry.attrs["start_time"] = timestamp()
271
+ entry["start_time"] = timestamp()
272
272
  writer = entry.create_group("writer")
273
273
  writer.attrs["NX_class"] = "NXnote"
274
274
  writer["status"] = "STARTING"
@@ -279,7 +279,7 @@ def writer_context(filename: str, scan_number: int) -> Iterator[h5py.Group]:
279
279
  else:
280
280
  writer["status"][()] = "SUCCEEDED"
281
281
  finally:
282
- entry.attrs["end_time"] = timestamp()
282
+ entry["end_time"] = timestamp()
283
283
 
284
284
 
285
285
  @contextmanager
@@ -80,6 +80,22 @@ def test_stream_unknown_encoding(data_store):
80
80
  Stream.create(data_store, "my_stream", model, None)
81
81
 
82
82
 
83
+ def test_multiple_calls_to_seal(data_store):
84
+ input = np.arange(1000)
85
+ rw_stream, _ = stream_pair(data_store, input.dtype)
86
+ rw_stream.send(input)
87
+
88
+ # make a second writing stream to test re-sealing from an unaware object
89
+ external_writer = Stream.create(
90
+ data_store, rw_stream.name, rw_stream._model, rw_stream._encoder
91
+ )
92
+
93
+ assert rw_stream.seal() == 1000
94
+ assert rw_stream.seal() == 1000
95
+ assert external_writer.seal() == 1000
96
+ assert external_writer.seal() == 1000
97
+
98
+
83
99
  def test_stream_chunk_slicing(data_store):
84
100
  data = np.arange(100)
85
101
  rw_stream, ro_stream = stream_pair(data_store, data.dtype)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blissdata
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Bliss data streaming client
5
5
  Home-page: https://gitlab.esrf.fr/bliss/bliss/-/tree/master/blissdata
6
6
  Author: BCU (ESRF)
@@ -15,8 +15,8 @@ Requires-Dist: beartype
15
15
  Requires-Dist: h5py<3.10,>=2.9
16
16
  Requires-Dist: msgpack
17
17
  Requires-Dist: numpy>=1.13
18
- Requires-Dist: redis>=4.6.0
19
- Requires-Dist: redis-om
18
+ Requires-Dist: redis<5,>=4.6.0
19
+ Requires-Dist: redis-om<0.3
20
20
  Requires-Dist: silx>=1.1.0
21
21
  Provides-Extra: tango
22
22
  Requires-Dist: pytango>=9.3.1; extra == "tango"
@@ -2,8 +2,8 @@ beartype
2
2
  h5py<3.10,>=2.9
3
3
  msgpack
4
4
  numpy>=1.13
5
- redis>=4.6.0
6
- redis-om
5
+ redis<5,>=4.6.0
6
+ redis-om<0.3
7
7
  silx>=1.1.0
8
8
 
9
9
  [beacon]
@@ -20,8 +20,8 @@ install_requires =
20
20
  h5py >=2.9, <3.10 # https://gitlab.esrf.fr/bliss/bliss/-/issues/3959
21
21
  msgpack
22
22
  numpy >=1.13
23
- redis >=4.6.0
24
- redis-om
23
+ redis >=4.6.0, <5
24
+ redis-om <0.3
25
25
  silx >=1.1.0
26
26
 
27
27
  [options.extras_require]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes