blissdata-lima2 2.0.1__tar.gz → 2.0.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blissdata-lima2
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: Lima2 plugin for blissdata
5
5
  Maintainer: BCU (ESRF)
6
6
  License-Expression: MIT
@@ -11,8 +11,8 @@ Classifier: Programming Language :: Python :: 3
11
11
  Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE.md
14
- Requires-Dist: blissdata~=2.3
15
- Requires-Dist: lima2-client~=4.0
14
+ Requires-Dist: blissdata~=2.4
15
+ Requires-Dist: lima2-client~=4.1
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: black; extra == "dev"
18
18
  Requires-Dist: flake8; extra == "dev"
@@ -0,0 +1,70 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "blissdata-lima2"
7
+ version = "2.0.3"
8
+ description = "Lima2 plugin for blissdata"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE.md"]
12
+ maintainers = [{ name = "BCU (ESRF)" }]
13
+ classifiers = [
14
+ "Intended Audience :: Science/Research",
15
+ "Programming Language :: Python :: 3",
16
+ ]
17
+ keywords = ["blissdata", "lima2"]
18
+ requires-python = ">=3.10"
19
+ dependencies = ["blissdata~=2.4", "lima2-client~=4.1"]
20
+
21
+ [project.urls]
22
+ Repository = "https://gitlab.esrf.fr/bliss/blissdata-lima2"
23
+
24
+ [project.optional-dependencies]
25
+ dev = ["black", "flake8", "mypy", "pytest", "pytest-cov", "pytest-redis"]
26
+
27
+ [project.entry-points.blissdata]
28
+ lima2 = "blissdata_lima2"
29
+
30
+ [tool.pixi.workspace]
31
+ channels = ["conda-forge", "esrf-bcu"]
32
+ platforms = ["linux-64"]
33
+
34
+ [tool.pixi.dependencies]
35
+ python = ">=3.10,<3.15"
36
+
37
+ [tool.pixi.pypi-dependencies]
38
+ blissdata-lima2 = { path = ".", editable = true }
39
+
40
+ [tool.pixi.environments]
41
+ default = { solve-group = "default" }
42
+ test = { features = ["test"], solve-group = "default" }
43
+ dev = { features = ["dev"], no-default-feature = true }
44
+ build = { features = ["build"], no-default-feature = true }
45
+
46
+ [dependency-groups]
47
+ test = ["blissdata-lima2[dev]"]
48
+ build = ["build"]
49
+
50
+ [tool.pixi.feature.test.dependencies]
51
+ redis-server = "7.*"
52
+ redisearch = ">=2.6.5"
53
+ redisjson = ">=2.4.5"
54
+
55
+ [tool.pixi.feature.test.tasks]
56
+ test = { cmd = "pytest -v", description = "Run tests" }
57
+
58
+ [tool.pixi.feature.dev.dependencies]
59
+ python = ">=3.10"
60
+
61
+ [tool.pixi.feature.dev.tasks]
62
+ style = { cmd = "black . --check", description = "Check the style of the source code" }
63
+ fix-style = { cmd = "black .", description = "Fixes the style of the source code" }
64
+ lint = { cmd = "flake8", description = "Check the linting" }
65
+
66
+ [tool.pixi.feature.build.dependencies]
67
+ python = ">=3.10"
68
+
69
+ [tool.pixi.feature.build.tasks]
70
+ build = { cmd = "python -m build .", description = "Build packages" }
@@ -10,6 +10,7 @@ from collections.abc import Sequence
10
10
 
11
11
  import lima2.client.services as l2s
12
12
  import numpy as np
13
+ from functools import cached_property
13
14
  from blissdata.exceptions import (
14
15
  EmptyViewException,
15
16
  EndOfStream,
@@ -52,18 +53,12 @@ def get_frame(
52
53
  class Lima2View(BaseView):
53
54
  def __init__(
54
55
  self,
55
- services: l2s.ConductorServices,
56
- acq_uuid: str,
57
- source: str,
56
+ stream: Lima2Stream,
58
57
  start: int,
59
58
  stop: int,
60
59
  ) -> None:
61
- self._services = services
62
- """Lima2 client services."""
63
- self._acq_uuid = acq_uuid
64
- """Lima2 acquisition id."""
65
- self._source = source
66
- """Frame source name."""
60
+ self._stream = stream
61
+ """Lima2 stream."""
67
62
  self._idx_range = range(start, stop)
68
63
  """Range of absolute frame indices accessible via this view."""
69
64
 
@@ -75,29 +70,10 @@ class Lima2View(BaseView):
75
70
  return len(self._idx_range)
76
71
 
77
72
  def get_data(self, start: int | None = None, stop: int | None = None) -> np.ndarray:
78
- frames = []
79
- for idx in self._idx_range[start:stop]:
80
- try:
81
- frames.append(
82
- get_frame(
83
- services=self._services,
84
- acq_uuid=self._acq_uuid,
85
- source=self._source,
86
- frame_idx=idx,
87
- ).array
88
- )
89
- except RuntimeError as e:
90
- # Raise if any frame can't be accessed
91
- raise IndexNoMoreThereError(
92
- f"Can't fetch {self._source} {self._idx_range[start:stop]}: {e}"
93
- ) from e
94
- except ValueError as e:
95
- # Frame source has no associated memory buffer (raw_frame)
96
- raise IndexNoMoreThereError(
97
- f"Can't fetch {self._source}: no associated live data"
98
- ) from e
99
-
100
- return np.asarray(frames)
73
+ stream_range = self._idx_range[start:stop]
74
+ return self._stream._fetch_online(
75
+ stream_range.start, stream_range.stop, stream_range.step
76
+ )
101
77
 
102
78
 
103
79
  class Lima2Stream(BaseStream, LimaDirectAccess):
@@ -130,12 +106,6 @@ class Lima2Stream(BaseStream, LimaDirectAccess):
130
106
  self._source = str(event_stream.info["source_name"])
131
107
  self._master_file: tuple[str, str] | None = event_stream.info["master_file"]
132
108
 
133
- self._services = l2s.init(
134
- hostname=str(event_stream.info["conductor_hostname"]),
135
- port=int(event_stream.info["conductor_port"]),
136
- )
137
- """Lima2 client session."""
138
-
139
109
  self._length = 0
140
110
  """Current number of accessible frames."""
141
111
 
@@ -145,6 +115,16 @@ class Lima2Stream(BaseStream, LimaDirectAccess):
145
115
  # BaseStream
146
116
  ####################################################################################
147
117
 
118
+ @cached_property
119
+ def _services(self):
120
+ """Lima2 client session."""
121
+ services = l2s.init(
122
+ hostname=str(self._event_stream.info["conductor_hostname"]),
123
+ port=int(self._event_stream.info["conductor_port"]),
124
+ )
125
+ services.handshake()
126
+ return services
127
+
148
128
  @property
149
129
  def kind(self) -> str:
150
130
  return "array"
@@ -260,9 +240,7 @@ class Lima2Stream(BaseStream, LimaDirectAccess):
260
240
  raise EmptyViewException
261
241
 
262
242
  return Lima2View(
263
- services=self._services,
264
- acq_uuid=self._acq_uuid,
265
- source=self._source,
243
+ stream=self,
266
244
  start=stop_idx - 1 if last_only else index,
267
245
  stop=stop_idx,
268
246
  )
@@ -335,7 +313,7 @@ class Lima2Stream(BaseStream, LimaDirectAccess):
335
313
  f"Can't fetch {self._source}: no associated live data"
336
314
  ) from e
337
315
 
338
- return np.asarray(frames)
316
+ return np.asarray(frames).reshape(-1, *self.shape)
339
317
 
340
318
  def _fetch_from_disk(
341
319
  self, filepath: str, datapath: str, start: int, stop: int, step: int
@@ -27,6 +27,16 @@ from blissdata_lima2 import Lima2Stream, Lima2View
27
27
  from blissdata_lima2.stream import get_frame
28
28
 
29
29
 
30
+ @pytest.fixture
31
+ def mock_handshake(monkeypatch):
32
+ def mock_handshake(self):
33
+ pass
34
+
35
+ monkeypatch.setattr(
36
+ "lima2.client.services.ConductorServices.handshake", mock_handshake
37
+ )
38
+
39
+
30
40
  def test_lima2_get_frame():
31
41
  services = Mock()
32
42
  uuid = str(uuid1())
@@ -56,45 +66,23 @@ def test_lima2_get_sparse_frame():
56
66
 
57
67
 
58
68
  def test_lima2_view(monkeypatch):
59
- services = Mock()
60
- uuid = str(uuid1())
69
+ stream = Mock()
61
70
  view = Lima2View(
62
- services=services,
63
- acq_uuid=uuid,
64
- source="cafe",
71
+ stream=stream,
65
72
  start=0,
66
73
  stop=42,
67
74
  )
68
75
  assert len(view) == 42
69
76
  assert view.index == 0
70
77
 
71
- frames = view.get_data()
72
- assert len(frames) == len(view)
73
-
74
- services.pipeline.get_frame.assert_has_calls(
75
- [call(uuid=uuid, source="cafe", frame_idx=i) for i in range(42)]
76
- )
77
-
78
- # RuntimeError
79
- def mock_get_frame_runtime_error(services, acq_uuid, source, frame_idx):
80
- raise RuntimeError("frame has left the buffer")
78
+ _ = view.get_data()
81
79
 
82
- monkeypatch.setattr(
83
- "blissdata_lima2.stream.get_frame", mock_get_frame_runtime_error
80
+ stream._fetch_online.assert_has_calls(
81
+ [
82
+ call(0, 42, 1),
83
+ ]
84
84
  )
85
85
 
86
- with pytest.raises(IndexNoMoreThereError):
87
- frames = view.get_data()
88
-
89
- # ValueError
90
- def mock_get_frame_value_error(services, acq_uuid, source, frame_idx):
91
- raise ValueError("no such frame buffer")
92
-
93
- monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame_value_error)
94
-
95
- with pytest.raises(IndexNoMoreThereError):
96
- frames = view.get_data()
97
-
98
86
 
99
87
  def test_lima2_protocol(data_store):
100
88
  uuid = str(uuid1())
@@ -120,7 +108,7 @@ def test_lima2_protocol(data_store):
120
108
 
121
109
 
122
110
  @pytest.fixture
123
- def lima2_stream(data_store):
111
+ def lima2_stream(data_store, mock_handshake):
124
112
  uuid = str(uuid1())
125
113
 
126
114
  stream_def = Lima2Stream.make_definition(
@@ -131,7 +119,7 @@ def lima2_stream(data_store):
131
119
  acq_uuid=uuid,
132
120
  master_file=None,
133
121
  dtype=np.float128, # fat pixels >:)
134
- shape=(4, 1024, 512),
122
+ shape=(),
135
123
  )
136
124
  model = data_store._stream_model(
137
125
  encoding=stream_def.encoder.info(), info=stream_def.info
@@ -140,162 +128,152 @@ def lima2_stream(data_store):
140
128
  event_stream = EventStream.create(data_store, stream_def.name, model)
141
129
  stream = Lima2Stream(event_stream=event_stream)
142
130
 
143
- return (stream_def, event_stream, stream)
131
+ return stream
144
132
 
145
133
 
146
134
  def test_lima2_stream_attributes(lima2_stream):
147
- stream_def, event_stream, stream = lima2_stream
148
-
149
- assert stream.plugin == "lima2"
150
- assert stream.shape == stream_def.info["shape"]
151
- assert stream.kind == "array"
152
- assert stream.dtype == stream._dtype
153
- assert stream._need_last_only(last_only=True)
154
- assert stream._need_last_only(last_only=False)
135
+ assert lima2_stream.plugin == "lima2"
136
+ assert lima2_stream.shape == lima2_stream.info["shape"]
137
+ assert lima2_stream.kind == "array"
138
+ assert lima2_stream.dtype == lima2_stream._dtype
139
+ assert lima2_stream._need_last_only(last_only=True)
140
+ assert lima2_stream._need_last_only(last_only=False)
155
141
 
156
142
 
157
143
  def test_lima2_stream_indexing(lima2_stream, monkeypatch):
158
- stream_def, event_stream, stream = lima2_stream
159
-
160
144
  # Feed the event stream
161
- event_stream.send(np.uint32(42))
162
- event_stream.join()
163
- assert len(stream) == 42
145
+ lima2_stream.send(np.uint32(42))
146
+ lima2_stream.join()
147
+ assert len(lima2_stream) == 42
164
148
 
165
- event_stream.send(np.uint32(123))
166
- event_stream.join()
167
- assert len(stream) == 123
149
+ lima2_stream.send(np.uint32(123))
150
+ lima2_stream.join()
151
+ assert len(lima2_stream) == 123
168
152
 
169
153
  mock_get_frame = Mock()
170
154
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
171
155
 
172
156
  # Indexing
173
- _ = stream[0]
157
+ _ = lima2_stream[0]
174
158
  mock_get_frame.assert_called_with(
175
- services=stream._services,
176
- acq_uuid=stream._acq_uuid,
159
+ services=lima2_stream._services,
160
+ acq_uuid=lima2_stream._acq_uuid,
177
161
  source="cafe",
178
162
  frame_idx=0,
179
163
  )
180
164
 
181
165
  with pytest.raises(IndexNotYetThereError):
182
- _ = stream[-1]
166
+ _ = lima2_stream[-1]
183
167
 
184
168
  with pytest.raises(IndexNotYetThereError):
185
- _ = stream[123123] # wayyy out of bounds, but we're not sealed yet
169
+ _ = lima2_stream[123123] # wayyy out of bounds, but we're not sealed yet
186
170
 
187
171
  with monkeypatch.context() as mpc:
188
- mpc.setattr(stream, "is_sealed", lambda: True)
172
+ mpc.setattr(lima2_stream, "is_sealed", lambda: True)
189
173
 
190
174
  with pytest.raises(IndexWontBeThereError):
191
- _ = stream[123123] # wayyy out of bounds and we're sealed
175
+ _ = lima2_stream[123123] # wayyy out of bounds and we're sealed
192
176
 
193
- event_stream.seal()
194
- event_stream.join()
177
+ lima2_stream.seal()
195
178
 
196
- _ = stream[-3]
179
+ _ = lima2_stream[-3]
197
180
  assert mock_get_frame.mock_calls[-1] == call(
198
- services=stream._services,
199
- acq_uuid=stream._acq_uuid,
181
+ services=lima2_stream._services,
182
+ acq_uuid=lima2_stream._acq_uuid,
200
183
  source="cafe",
201
184
  frame_idx=123 - 3,
202
185
  )
203
186
 
204
187
 
205
188
  def test_lima2_stream_slicing(lima2_stream, monkeypatch):
206
- stream_def, event_stream, stream = lima2_stream
207
-
208
- # Feed the event stream
209
- event_stream.send(np.uint32(42))
210
- event_stream.join()
211
- assert len(stream) == 42
189
+ # Feed the stream
190
+ lima2_stream.send(np.uint32(42))
191
+ lima2_stream.join()
192
+ assert len(lima2_stream) == 42
212
193
 
213
- event_stream.send(np.uint32(123))
214
- event_stream.join()
215
- assert len(stream) == 123
194
+ lima2_stream.send(np.uint32(123))
195
+ lima2_stream.join()
196
+ assert len(lima2_stream) == 123
216
197
 
217
198
  mock_get_frame = Mock()
218
199
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
219
200
 
220
201
  # Slicing
221
- _ = stream[:3]
202
+ _ = lima2_stream[:3]
222
203
  assert mock_get_frame.mock_calls[-3:] == [
223
204
  call(
224
- services=stream._services,
225
- acq_uuid=stream._acq_uuid,
205
+ services=lima2_stream._services,
206
+ acq_uuid=lima2_stream._acq_uuid,
226
207
  source="cafe",
227
208
  frame_idx=0,
228
209
  ),
229
210
  call(
230
- services=stream._services,
231
- acq_uuid=stream._acq_uuid,
211
+ services=lima2_stream._services,
212
+ acq_uuid=lima2_stream._acq_uuid,
232
213
  source="cafe",
233
214
  frame_idx=1,
234
215
  ),
235
216
  call(
236
- services=stream._services,
237
- acq_uuid=stream._acq_uuid,
217
+ services=lima2_stream._services,
218
+ acq_uuid=lima2_stream._acq_uuid,
238
219
  source="cafe",
239
220
  frame_idx=2,
240
221
  ),
241
222
  ]
242
223
 
243
224
  with pytest.raises(IndexNotYetThereError):
244
- _ = stream[-2:]
225
+ _ = lima2_stream[-2:]
245
226
 
246
227
  with pytest.raises(IndexNotYetThereError):
247
- _ = stream[:-2]
228
+ _ = lima2_stream[:-2]
248
229
 
249
230
  with pytest.raises(TypeError):
250
- _ = stream["hi :)"]
231
+ _ = lima2_stream["hi :)"]
251
232
 
252
- event_stream.seal()
253
- event_stream.join()
233
+ lima2_stream.seal()
254
234
 
255
235
  # Now slicing/indexing from end is ok
256
- _ = stream[-2:]
236
+ _ = lima2_stream[-2:]
257
237
  assert mock_get_frame.mock_calls[-2:] == [
258
238
  call(
259
- services=stream._services,
260
- acq_uuid=stream._acq_uuid,
239
+ services=lima2_stream._services,
240
+ acq_uuid=lima2_stream._acq_uuid,
261
241
  source="cafe",
262
242
  frame_idx=123 - 2,
263
243
  ),
264
244
  call(
265
- services=stream._services,
266
- acq_uuid=stream._acq_uuid,
245
+ services=lima2_stream._services,
246
+ acq_uuid=lima2_stream._acq_uuid,
267
247
  source="cafe",
268
248
  frame_idx=123 - 1,
269
249
  ),
270
250
  ]
271
251
 
272
- _ = stream[:]
252
+ _ = lima2_stream[:]
273
253
  assert mock_get_frame.mock_calls[-123:] == [
274
254
  call(
275
- services=stream._services,
276
- acq_uuid=stream._acq_uuid,
255
+ services=lima2_stream._services,
256
+ acq_uuid=lima2_stream._acq_uuid,
277
257
  source="cafe",
278
258
  frame_idx=i,
279
259
  )
280
260
  for i in range(123)
281
261
  ]
282
262
 
283
- _ = stream.get_last_live_image()
263
+ _ = lima2_stream.get_last_live_image()
284
264
  mock_get_frame.assert_called_with(
285
- services=stream._services,
286
- acq_uuid=stream._acq_uuid,
265
+ services=lima2_stream._services,
266
+ acq_uuid=lima2_stream._acq_uuid,
287
267
  source="cafe",
288
268
  frame_idx=-1,
289
269
  )
290
270
 
291
271
 
292
272
  def test_lima2_stream_build_view(lima2_stream, monkeypatch):
293
- stream_def, event_stream, stream = lima2_stream
294
-
295
273
  mock_get_frame = Mock()
296
274
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
297
275
 
298
- view = stream._build_view_from_events(
276
+ view = lima2_stream._build_view_from_events(
299
277
  index=0,
300
278
  events=EventRange(
301
279
  index=0,
@@ -307,7 +285,7 @@ def test_lima2_stream_build_view(lima2_stream, monkeypatch):
307
285
  )
308
286
  assert view._idx_range == range(0, 42)
309
287
 
310
- view = stream._build_view_from_events(
288
+ view = lima2_stream._build_view_from_events(
311
289
  index=0,
312
290
  events=EventRange(
313
291
  index=0,
@@ -321,7 +299,7 @@ def test_lima2_stream_build_view(lima2_stream, monkeypatch):
321
299
 
322
300
  with pytest.raises(EmptyViewException):
323
301
  # index >= data[-1]
324
- view = stream._build_view_from_events(
302
+ view = lima2_stream._build_view_from_events(
325
303
  index=42,
326
304
  events=EventRange(
327
305
  index=0,
@@ -334,23 +312,21 @@ def test_lima2_stream_build_view(lima2_stream, monkeypatch):
334
312
 
335
313
 
336
314
  def test_lima2_stream_fetch_frames(lima2_stream, monkeypatch):
337
- stream_def, event_stream, stream = lima2_stream
338
-
339
315
  mock_get_frame = Mock()
340
316
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
341
317
 
342
318
  with monkeypatch.context() as mpc:
343
319
  # Stream sealed, has master file
344
320
  mock_fetch = Mock()
345
- mpc.setattr(stream, "_fetch_from_disk", mock_fetch)
346
- mpc.setattr(stream, "is_sealed", lambda: True)
321
+ mpc.setattr(lima2_stream, "_fetch_from_disk", mock_fetch)
322
+ mpc.setattr(lima2_stream, "is_sealed", lambda: True)
347
323
  mpc.setattr(
348
- stream,
324
+ lima2_stream,
349
325
  "_master_file",
350
326
  ("/path/to/master.h5", "entry/instrument/detector/dataset"),
351
327
  )
352
328
 
353
- stream._fetch_frames(start=0, stop=42, step=3)
329
+ lima2_stream._fetch_frames(start=0, stop=42, step=3)
354
330
 
355
331
  mock_fetch.assert_called_once_with(
356
332
  filepath="/path/to/master.h5",
@@ -363,11 +339,11 @@ def test_lima2_stream_fetch_frames(lima2_stream, monkeypatch):
363
339
  with monkeypatch.context() as mpc:
364
340
  # Stream sealed, no master file
365
341
  mock_fetch = Mock()
366
- mpc.setattr(stream, "_fetch_online", mock_fetch)
367
- mpc.setattr(stream, "is_sealed", lambda: True)
368
- mpc.setattr(stream, "_master_file", None)
342
+ mpc.setattr(lima2_stream, "_fetch_online", mock_fetch)
343
+ mpc.setattr(lima2_stream, "is_sealed", lambda: True)
344
+ mpc.setattr(lima2_stream, "_master_file", None)
369
345
 
370
- stream._fetch_frames(start=0, stop=42, step=3)
346
+ lima2_stream._fetch_frames(start=0, stop=42, step=3)
371
347
 
372
348
  mock_fetch.assert_called_once_with(
373
349
  start=0,
@@ -378,10 +354,10 @@ def test_lima2_stream_fetch_frames(lima2_stream, monkeypatch):
378
354
  with monkeypatch.context() as mpc:
379
355
  # Stream not sealed
380
356
  mock_fetch = Mock()
381
- mpc.setattr(stream, "_fetch_online", mock_fetch)
382
- mpc.setattr(stream, "is_sealed", lambda: False)
357
+ mpc.setattr(lima2_stream, "_fetch_online", mock_fetch)
358
+ mpc.setattr(lima2_stream, "is_sealed", lambda: False)
383
359
 
384
- stream._fetch_frames(start=0, stop=42, step=3)
360
+ lima2_stream._fetch_frames(start=0, stop=42, step=3)
385
361
 
386
362
  mock_fetch.assert_called_once_with(
387
363
  start=0,
@@ -391,19 +367,17 @@ def test_lima2_stream_fetch_frames(lima2_stream, monkeypatch):
391
367
 
392
368
 
393
369
  def test_lima2_stream_fetch_online(lima2_stream, monkeypatch):
394
- stream_def, event_stream, stream = lima2_stream
395
-
396
370
  mock_get_frame = Mock()
397
371
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
398
372
 
399
- stream._fetch_online(start=0, stop=42, step=3)
373
+ lima2_stream._fetch_online(start=0, stop=42, step=3)
400
374
 
401
375
  mock_get_frame.assert_has_calls(
402
376
  [
403
377
  call(
404
- services=stream._services,
405
- acq_uuid=stream._acq_uuid,
406
- source=stream._source,
378
+ services=lima2_stream._services,
379
+ acq_uuid=lima2_stream._acq_uuid,
380
+ source=lima2_stream._source,
407
381
  frame_idx=idx,
408
382
  )
409
383
  for idx in range(0, 42, 3)
@@ -419,7 +393,7 @@ def test_lima2_stream_fetch_online(lima2_stream, monkeypatch):
419
393
  )
420
394
 
421
395
  with pytest.raises(IndexNoMoreThereError):
422
- _ = stream._fetch_online(start=0, stop=1, step=1)
396
+ _ = lima2_stream._fetch_online(start=0, stop=1, step=1)
423
397
 
424
398
  # ValueError
425
399
  def mock_get_frame_value_error(services, acq_uuid, source, frame_idx):
@@ -428,16 +402,14 @@ def test_lima2_stream_fetch_online(lima2_stream, monkeypatch):
428
402
  monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame_value_error)
429
403
 
430
404
  with pytest.raises(IndexNoMoreThereError):
431
- _ = stream._fetch_online(start=0, stop=1, step=1)
405
+ _ = lima2_stream._fetch_online(start=0, stop=1, step=1)
432
406
 
433
407
 
434
408
  def test_lima2_stream_fetch_offline(lima2_stream, monkeypatch):
435
- stream_def, event_stream, stream = lima2_stream
436
-
437
409
  mock_dynamic_hdf5 = MagicMock()
438
410
  monkeypatch.setattr("blissdata_lima2.stream.dynamic_hdf5", mock_dynamic_hdf5)
439
411
 
440
- stream._fetch_from_disk(
412
+ lima2_stream._fetch_from_disk(
441
413
  filepath="/path/to/master.h5",
442
414
  datapath="entry/instrument/detector/dataset",
443
415
  start=0,
@@ -446,3 +418,55 @@ def test_lima2_stream_fetch_offline(lima2_stream, monkeypatch):
446
418
  )
447
419
 
448
420
  mock_dynamic_hdf5.File.assert_called_once()
421
+
422
+
423
+ def test_lima2_stream_reshape(data_store, mock_handshake, monkeypatch):
424
+ """Verify proper reshaping of the frame on online frame fetch."""
425
+
426
+ stream_def = Lima2Stream.make_definition(
427
+ name="device:cafe",
428
+ source_name="cafe",
429
+ conductor_hostname="www.lima2.org",
430
+ conductor_port=12345,
431
+ acq_uuid=uuid1(),
432
+ master_file=None,
433
+ dtype=np.uint32,
434
+ shape=(13, 37), # note 2D
435
+ )
436
+ lima2_stream = Lima2Stream(
437
+ event_stream=EventStream.create(
438
+ data_store=data_store,
439
+ name=stream_def.name,
440
+ model=data_store._stream_model(
441
+ encoding=stream_def.encoder.info(), info=stream_def.info
442
+ ),
443
+ )
444
+ )
445
+
446
+ def mock_get_frame_3d(services, acq_uuid, source, frame_idx):
447
+ return ImageData(array=np.zeros((1, 13, 37)), frame_id=0, acq_tag=None)
448
+
449
+ monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame_3d)
450
+
451
+ data = lima2_stream._fetch_online(start=0, stop=4, step=1)
452
+ assert data.shape == (4, 13, 37)
453
+
454
+
455
+ def test_lima2_client_lazy_init(data_store, monkeypatch, lima2_stream):
456
+ class InitForbiddenError(Exception):
457
+ pass
458
+
459
+ def mock_client_init(*args, **kwargs):
460
+ raise InitForbiddenError()
461
+
462
+ monkeypatch.setattr("lima2.client.services.init", mock_client_init)
463
+
464
+ lima2_stream.send(np.uint32(42))
465
+ lima2_stream.join()
466
+ view = lima2_stream.cursor().read(block=False)
467
+
468
+ with pytest.raises(InitForbiddenError):
469
+ view.get_data()
470
+
471
+ with pytest.raises(InitForbiddenError):
472
+ lima2_stream[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blissdata-lima2
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: Lima2 plugin for blissdata
5
5
  Maintainer: BCU (ESRF)
6
6
  License-Expression: MIT
@@ -11,8 +11,8 @@ Classifier: Programming Language :: Python :: 3
11
11
  Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE.md
14
- Requires-Dist: blissdata~=2.3
15
- Requires-Dist: lima2-client~=4.0
14
+ Requires-Dist: blissdata~=2.4
15
+ Requires-Dist: lima2-client~=4.1
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: black; extra == "dev"
18
18
  Requires-Dist: flake8; extra == "dev"
@@ -1,5 +1,5 @@
1
- blissdata~=2.3
2
- lima2-client~=4.0
1
+ blissdata~=2.4
2
+ lima2-client~=4.1
3
3
 
4
4
  [dev]
5
5
  black
@@ -1,28 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "blissdata-lima2"
7
- version = "2.0.1"
8
- description = "Lima2 plugin for blissdata"
9
- readme = "README.md"
10
- license = "MIT"
11
- license-files = ["LICENSE.md"]
12
- maintainers = [{ name = "BCU (ESRF)" }]
13
- classifiers = [
14
- "Intended Audience :: Science/Research",
15
- "Programming Language :: Python :: 3",
16
- ]
17
- keywords = ["blissdata", "lima2"]
18
- requires-python = ">=3.10"
19
- dependencies = ["blissdata~=2.3", "lima2-client~=4.0"]
20
-
21
- [project.urls]
22
- Repository = "https://gitlab.esrf.fr/bliss/blissdata-lima2"
23
-
24
- [project.optional-dependencies]
25
- dev = ["black", "flake8", "mypy", "pytest", "pytest-cov", "pytest-redis"]
26
-
27
- [project.entry-points.blissdata]
28
- lima2 = "blissdata_lima2"