blissdata-lima2 2.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.
- blissdata_lima2-2.0/LICENSE.md +7 -0
- blissdata_lima2-2.0/PKG-INFO +24 -0
- blissdata_lima2-2.0/README.md +3 -0
- blissdata_lima2-2.0/pyproject.toml +25 -0
- blissdata_lima2-2.0/setup.cfg +4 -0
- blissdata_lima2-2.0/src/blissdata_lima2/__init__.py +4 -0
- blissdata_lima2-2.0/src/blissdata_lima2/stream.py +254 -0
- blissdata_lima2-2.0/src/blissdata_lima2/tests/__init__.py +0 -0
- blissdata_lima2-2.0/src/blissdata_lima2/tests/conftest.py +1 -0
- blissdata_lima2-2.0/src/blissdata_lima2/tests/test_lima2_stream.py +178 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/PKG-INFO +24 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/SOURCES.txt +14 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/dependency_links.txt +1 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/entry_points.txt +2 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/requires.txt +8 -0
- blissdata_lima2-2.0/src/blissdata_lima2.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blissdata-lima2
|
|
3
|
+
Version: 2.0
|
|
4
|
+
Summary: Lima2 plugin for blissdata
|
|
5
|
+
Maintainer: BCU (ESRF)
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: blissdata,lima2
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE.md
|
|
13
|
+
Requires-Dist: blissdata~=2.3
|
|
14
|
+
Requires-Dist: lima2-client~=4.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: black; extra == "dev"
|
|
17
|
+
Requires-Dist: flake8; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-redis; extra == "dev"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
## blissdata-lima2
|
|
23
|
+
|
|
24
|
+
Lima2 plugin for blissdata.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "blissdata-lima2"
|
|
7
|
+
version = "2.0"
|
|
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.optional-dependencies]
|
|
22
|
+
dev = ["black", "flake8", "pytest", "pytest-redis"]
|
|
23
|
+
|
|
24
|
+
[project.entry-points.blissdata]
|
|
25
|
+
lima2 = "blissdata_lima2"
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# This file is part of the bliss project
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
|
+
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import lima2.client.services as l2s
|
|
12
|
+
import numpy as np
|
|
13
|
+
from blissdata.exceptions import (
|
|
14
|
+
EmptyViewException,
|
|
15
|
+
EndOfStream,
|
|
16
|
+
IndexNoMoreThereError,
|
|
17
|
+
IndexNotYetThereError,
|
|
18
|
+
)
|
|
19
|
+
from blissdata.lima.image_utils import ImageData
|
|
20
|
+
from blissdata.streams import (
|
|
21
|
+
BaseStream,
|
|
22
|
+
BaseView,
|
|
23
|
+
EventRange,
|
|
24
|
+
EventStream,
|
|
25
|
+
StreamDefinition,
|
|
26
|
+
)
|
|
27
|
+
from blissdata.streams.default import Stream
|
|
28
|
+
from blissdata.streams.encoding.numeric import NumericStreamEncoder
|
|
29
|
+
from blissdata.streams.lima.stream import LimaDirectAccess
|
|
30
|
+
from lima2.common.devencoded.sparse_frame import SparseFrame
|
|
31
|
+
from numpy.typing import DTypeLike
|
|
32
|
+
|
|
33
|
+
_logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_frame(
|
|
37
|
+
services: l2s.ConductorServices,
|
|
38
|
+
acq_uuid: str,
|
|
39
|
+
source: str,
|
|
40
|
+
frame_idx: int,
|
|
41
|
+
) -> ImageData:
|
|
42
|
+
frm = services.pipeline.get_frame(frame_idx=frame_idx, source=source, uuid=acq_uuid)
|
|
43
|
+
|
|
44
|
+
if isinstance(frm, SparseFrame):
|
|
45
|
+
frm = frm.densify()
|
|
46
|
+
|
|
47
|
+
return ImageData(array=frm.data, frame_id=frm.idx, acq_tag=None)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Lima2View(BaseView):
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
services: l2s.ConductorServices,
|
|
54
|
+
acq_uuid: str,
|
|
55
|
+
source: str,
|
|
56
|
+
start: int,
|
|
57
|
+
stop: int,
|
|
58
|
+
) -> None:
|
|
59
|
+
self._services = services
|
|
60
|
+
"""Lima2 client services."""
|
|
61
|
+
self._acq_uuid = acq_uuid
|
|
62
|
+
"""Lima2 acquisition id."""
|
|
63
|
+
self._source = source
|
|
64
|
+
"""Frame source name."""
|
|
65
|
+
self._idx_range = range(start, stop)
|
|
66
|
+
"""Range of absolute frame indices accessible via this view."""
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def index(self) -> int:
|
|
70
|
+
return self._idx_range.start
|
|
71
|
+
|
|
72
|
+
def __len__(self) -> int:
|
|
73
|
+
return len(self._idx_range)
|
|
74
|
+
|
|
75
|
+
def get_data(
|
|
76
|
+
self, start: int | None = None, stop: int | None = None
|
|
77
|
+
) -> list[ImageData]:
|
|
78
|
+
try:
|
|
79
|
+
return [
|
|
80
|
+
get_frame(
|
|
81
|
+
services=self._services,
|
|
82
|
+
acq_uuid=self._acq_uuid,
|
|
83
|
+
source=self._source,
|
|
84
|
+
frame_idx=idx,
|
|
85
|
+
)
|
|
86
|
+
for idx in self._idx_range[start:stop]
|
|
87
|
+
]
|
|
88
|
+
except RuntimeError as e:
|
|
89
|
+
raise IndexNoMoreThereError(
|
|
90
|
+
f"Can't fetch {self._source} {self._idx_range[start:stop].start} "
|
|
91
|
+
f"to {self._idx_range[start:stop].stop}: {e}"
|
|
92
|
+
) from e
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Lima2Stream(BaseStream, LimaDirectAccess):
|
|
96
|
+
"""Stream of Lima2 frames.
|
|
97
|
+
|
|
98
|
+
Frames don't actually transit inside the stream. The stream length can be
|
|
99
|
+
queried to determine the number of accessible frames.
|
|
100
|
+
|
|
101
|
+
Indexing or slicing the stream attempts to fetch frames directly from the
|
|
102
|
+
Lima2 backend.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
PROTOCOL_VERSION = 2
|
|
106
|
+
|
|
107
|
+
def __init__(self, event_stream: EventStream) -> None:
|
|
108
|
+
BaseStream.__init__(self, event_stream)
|
|
109
|
+
|
|
110
|
+
_logger.debug(f"Instantiate Lima2Stream with {event_stream.info=}")
|
|
111
|
+
|
|
112
|
+
if event_stream.info["protocol_version"] != Lima2Stream.PROTOCOL_VERSION:
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
f"Lima2 protocol version mismatch "
|
|
115
|
+
f"(expected {Lima2Stream.PROTOCOL_VERSION}, "
|
|
116
|
+
f"got {event_stream.info['protocol_version']})"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
self._dtype = np.dtype(event_stream.info["dtype"])
|
|
120
|
+
self._shape = tuple(event_stream.info["shape"])
|
|
121
|
+
self._acq_uuid = str(event_stream.info["acq_uuid"])
|
|
122
|
+
self._source = str(event_stream.info["source_name"])
|
|
123
|
+
|
|
124
|
+
self._services = l2s.init(
|
|
125
|
+
hostname=str(event_stream.info["conductor_hostname"]),
|
|
126
|
+
port=int(event_stream.info["conductor_port"]),
|
|
127
|
+
)
|
|
128
|
+
"""Lima2 client session."""
|
|
129
|
+
|
|
130
|
+
self._length = 0
|
|
131
|
+
"""Current number of accessible frames."""
|
|
132
|
+
|
|
133
|
+
self._cursor = Stream(event_stream).cursor()
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def kind(self):
|
|
137
|
+
return "array"
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def make_definition(
|
|
141
|
+
name: str,
|
|
142
|
+
source_name: str,
|
|
143
|
+
conductor_hostname: str,
|
|
144
|
+
conductor_port: int,
|
|
145
|
+
acq_uuid: str,
|
|
146
|
+
master_file: tuple[str, str] | None,
|
|
147
|
+
dtype: DTypeLike,
|
|
148
|
+
shape: Sequence,
|
|
149
|
+
) -> StreamDefinition:
|
|
150
|
+
info = {
|
|
151
|
+
"plugin": "lima2",
|
|
152
|
+
"dtype": np.dtype(dtype).name,
|
|
153
|
+
"shape": shape,
|
|
154
|
+
"protocol_version": Lima2Stream.PROTOCOL_VERSION,
|
|
155
|
+
"acq_uuid": acq_uuid,
|
|
156
|
+
"source_name": source_name,
|
|
157
|
+
"conductor_hostname": conductor_hostname,
|
|
158
|
+
"conductor_port": conductor_port,
|
|
159
|
+
"master_file": master_file,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return StreamDefinition(name, info, NumericStreamEncoder(np.uint32))
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def plugin(self) -> str:
|
|
166
|
+
return "lima2"
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def dtype(self) -> np.dtype:
|
|
170
|
+
return self._dtype
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def shape(self) -> tuple[int, ...]:
|
|
174
|
+
return self._shape
|
|
175
|
+
|
|
176
|
+
def __len__(self) -> int:
|
|
177
|
+
try:
|
|
178
|
+
view = self._cursor.read(block=False, last_only=True)
|
|
179
|
+
except EndOfStream:
|
|
180
|
+
view = None
|
|
181
|
+
|
|
182
|
+
if view is not None:
|
|
183
|
+
last_status = view.get_data()[0]
|
|
184
|
+
self._length = int(last_status)
|
|
185
|
+
return self._length
|
|
186
|
+
|
|
187
|
+
def __getitem__(self, key: int | slice) -> ImageData | list[ImageData]:
|
|
188
|
+
idx_range = range(len(self))
|
|
189
|
+
if type(key) is int:
|
|
190
|
+
if key < 0 and not self.is_sealed():
|
|
191
|
+
raise IndexNotYetThereError(
|
|
192
|
+
"Can't index from end of stream until it is sealed"
|
|
193
|
+
)
|
|
194
|
+
return get_frame(
|
|
195
|
+
services=self._services,
|
|
196
|
+
acq_uuid=self._acq_uuid,
|
|
197
|
+
source=self._source,
|
|
198
|
+
frame_idx=idx_range[key],
|
|
199
|
+
)
|
|
200
|
+
elif type(key) is slice:
|
|
201
|
+
if not self.is_sealed() and ((key.start or 0) < 0 or (key.stop or 0) < 0):
|
|
202
|
+
raise IndexNotYetThereError(
|
|
203
|
+
"Can't slice from end of stream until it is sealed"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return [
|
|
207
|
+
get_frame(
|
|
208
|
+
services=self._services,
|
|
209
|
+
acq_uuid=self._acq_uuid,
|
|
210
|
+
source=self._source,
|
|
211
|
+
frame_idx=idx,
|
|
212
|
+
)
|
|
213
|
+
for idx in idx_range[key]
|
|
214
|
+
]
|
|
215
|
+
else:
|
|
216
|
+
raise TypeError(f"{type(key)}")
|
|
217
|
+
|
|
218
|
+
def _need_last_only(self, last_only: bool) -> bool:
|
|
219
|
+
# Lima2 event stream represents current progress
|
|
220
|
+
# -> only the latest one is relevant.
|
|
221
|
+
return True
|
|
222
|
+
|
|
223
|
+
def _build_view_from_events(
|
|
224
|
+
self, index: int, events: EventRange, last_only: bool
|
|
225
|
+
) -> Lima2View:
|
|
226
|
+
"""
|
|
227
|
+
Build a Lima2View to access a slice of frames which starts at `index`,
|
|
228
|
+
and ends at the most recent frame according to `events`.
|
|
229
|
+
"""
|
|
230
|
+
_logger.debug(f"{self.name}: {index=} -> {events=}")
|
|
231
|
+
|
|
232
|
+
# events.data[-1] corresponds to the current number of contiguous frames
|
|
233
|
+
# accessible from the lima2 backend.
|
|
234
|
+
stop_idx = events.data[-1]
|
|
235
|
+
|
|
236
|
+
if stop_idx <= index:
|
|
237
|
+
# no new image despite new events
|
|
238
|
+
raise EmptyViewException
|
|
239
|
+
|
|
240
|
+
return Lima2View(
|
|
241
|
+
services=self._services,
|
|
242
|
+
acq_uuid=self._acq_uuid,
|
|
243
|
+
source=self._source,
|
|
244
|
+
start=stop_idx - 1 if last_only else index,
|
|
245
|
+
stop=stop_idx,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def get_last_live_image(self) -> ImageData:
|
|
249
|
+
return get_frame(
|
|
250
|
+
services=self._services,
|
|
251
|
+
acq_uuid=self._acq_uuid,
|
|
252
|
+
source=self._source,
|
|
253
|
+
frame_idx=-1,
|
|
254
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from blissdata.tests.conftest import redis_db, redis_url, data_store # noqa F401
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# This file is part of the bliss project
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
|
+
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
|
+
|
|
8
|
+
"""Unit test suite for Lima2 stream and view (streams/lima2.py)."""
|
|
9
|
+
|
|
10
|
+
from unittest.mock import Mock, call
|
|
11
|
+
from uuid import uuid1
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pytest
|
|
15
|
+
from blissdata.exceptions import IndexNotYetThereError
|
|
16
|
+
from blissdata.lima.image_utils import ImageData
|
|
17
|
+
from blissdata.streams import EventStream
|
|
18
|
+
|
|
19
|
+
from blissdata_lima2 import Lima2Stream, Lima2View
|
|
20
|
+
from blissdata_lima2.stream import get_frame
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_lima2_get_frame():
|
|
24
|
+
services = Mock()
|
|
25
|
+
uuid = str(uuid1())
|
|
26
|
+
frm = get_frame(services=services, acq_uuid=uuid, source="cafe", frame_idx=123)
|
|
27
|
+
services.pipeline.get_frame.assert_called_with(
|
|
28
|
+
frame_idx=123, source="cafe", uuid=uuid
|
|
29
|
+
)
|
|
30
|
+
assert type(frm) is ImageData
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_lima2_view():
|
|
34
|
+
services = Mock()
|
|
35
|
+
uuid = str(uuid1())
|
|
36
|
+
view = Lima2View(
|
|
37
|
+
services=services,
|
|
38
|
+
acq_uuid=uuid,
|
|
39
|
+
source="cafe",
|
|
40
|
+
start=0,
|
|
41
|
+
stop=42,
|
|
42
|
+
)
|
|
43
|
+
assert len(view) == 42
|
|
44
|
+
assert view.index == 0
|
|
45
|
+
|
|
46
|
+
frames = view.get_data()
|
|
47
|
+
assert len(frames) == len(view)
|
|
48
|
+
|
|
49
|
+
services.pipeline.get_frame.assert_has_calls(
|
|
50
|
+
[call(uuid=uuid, source="cafe", frame_idx=i) for i in range(42)]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_lima2_protocol(data_store):
|
|
55
|
+
uuid = str(uuid1())
|
|
56
|
+
|
|
57
|
+
stream_def = Lima2Stream.make_definition(
|
|
58
|
+
name="device:cafe",
|
|
59
|
+
source_name="cafe",
|
|
60
|
+
conductor_hostname="www.lima2.org",
|
|
61
|
+
conductor_port=12345,
|
|
62
|
+
acq_uuid=uuid,
|
|
63
|
+
master_file=None,
|
|
64
|
+
dtype=np.float128, # fat pixels >:)
|
|
65
|
+
shape=(4, 1024, 512),
|
|
66
|
+
)
|
|
67
|
+
model = data_store._stream_model(
|
|
68
|
+
encoding=stream_def.encoder.info(), info=stream_def.info
|
|
69
|
+
)
|
|
70
|
+
model.info["protocol_version"] = 1 # hack the protocol number
|
|
71
|
+
event_stream = EventStream.create(data_store, stream_def.name, model)
|
|
72
|
+
|
|
73
|
+
with pytest.raises(RuntimeError):
|
|
74
|
+
_ = Lima2Stream(event_stream=event_stream)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_lima2_stream(data_store, monkeypatch):
|
|
78
|
+
uuid = str(uuid1())
|
|
79
|
+
|
|
80
|
+
stream_def = Lima2Stream.make_definition(
|
|
81
|
+
name="device:cafe",
|
|
82
|
+
source_name="cafe",
|
|
83
|
+
conductor_hostname="www.lima2.org",
|
|
84
|
+
conductor_port=12345,
|
|
85
|
+
acq_uuid=uuid,
|
|
86
|
+
master_file=None,
|
|
87
|
+
dtype=np.float128, # fat pixels >:)
|
|
88
|
+
shape=(4, 1024, 512),
|
|
89
|
+
)
|
|
90
|
+
model = data_store._stream_model(
|
|
91
|
+
encoding=stream_def.encoder.info(), info=stream_def.info
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
event_stream = EventStream.create(data_store, stream_def.name, model)
|
|
95
|
+
stream = Lima2Stream(event_stream=event_stream)
|
|
96
|
+
|
|
97
|
+
assert stream.plugin == "lima2"
|
|
98
|
+
assert stream.shape == stream_def.info["shape"]
|
|
99
|
+
|
|
100
|
+
# Feed the event stream
|
|
101
|
+
event_stream.send(np.uint32(42))
|
|
102
|
+
event_stream.join()
|
|
103
|
+
assert len(stream) == 42
|
|
104
|
+
|
|
105
|
+
event_stream.send(np.uint32(123))
|
|
106
|
+
event_stream.join()
|
|
107
|
+
assert len(stream) == 123
|
|
108
|
+
|
|
109
|
+
mock_get_frame = Mock()
|
|
110
|
+
monkeypatch.setattr("blissdata_lima2.stream.get_frame", mock_get_frame)
|
|
111
|
+
|
|
112
|
+
# Indexing
|
|
113
|
+
_ = stream[0]
|
|
114
|
+
mock_get_frame.assert_called_with(
|
|
115
|
+
services=stream._services,
|
|
116
|
+
acq_uuid=uuid,
|
|
117
|
+
source="cafe",
|
|
118
|
+
frame_idx=0,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
with pytest.raises(IndexNotYetThereError):
|
|
122
|
+
_ = stream[-1]
|
|
123
|
+
|
|
124
|
+
# Slicing
|
|
125
|
+
_ = stream[:3]
|
|
126
|
+
assert mock_get_frame.mock_calls[-3:] == [
|
|
127
|
+
call(services=stream._services, acq_uuid=uuid, source="cafe", frame_idx=0),
|
|
128
|
+
call(services=stream._services, acq_uuid=uuid, source="cafe", frame_idx=1),
|
|
129
|
+
call(services=stream._services, acq_uuid=uuid, source="cafe", frame_idx=2),
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
with pytest.raises(IndexNotYetThereError):
|
|
133
|
+
_ = stream[-2:]
|
|
134
|
+
|
|
135
|
+
with pytest.raises(IndexNotYetThereError):
|
|
136
|
+
_ = stream[:-2]
|
|
137
|
+
|
|
138
|
+
event_stream.seal()
|
|
139
|
+
event_stream.join()
|
|
140
|
+
|
|
141
|
+
# Now slicing/indexing from end is ok
|
|
142
|
+
_ = stream[-2:]
|
|
143
|
+
assert mock_get_frame.mock_calls[-2:] == [
|
|
144
|
+
call(
|
|
145
|
+
services=stream._services,
|
|
146
|
+
acq_uuid=uuid,
|
|
147
|
+
source="cafe",
|
|
148
|
+
frame_idx=123 - 2,
|
|
149
|
+
),
|
|
150
|
+
call(
|
|
151
|
+
services=stream._services,
|
|
152
|
+
acq_uuid=uuid,
|
|
153
|
+
source="cafe",
|
|
154
|
+
frame_idx=123 - 1,
|
|
155
|
+
),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
_ = stream[-3]
|
|
159
|
+
assert mock_get_frame.mock_calls[-1] == call(
|
|
160
|
+
services=stream._services,
|
|
161
|
+
acq_uuid=uuid,
|
|
162
|
+
source="cafe",
|
|
163
|
+
frame_idx=123 - 3,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
_ = stream[:]
|
|
167
|
+
assert mock_get_frame.mock_calls[-123:] == [
|
|
168
|
+
call(services=stream._services, acq_uuid=uuid, source="cafe", frame_idx=i)
|
|
169
|
+
for i in range(123)
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
_ = stream.get_last_live_image()
|
|
173
|
+
mock_get_frame.assert_called_with(
|
|
174
|
+
services=stream._services,
|
|
175
|
+
acq_uuid=uuid,
|
|
176
|
+
source="cafe",
|
|
177
|
+
frame_idx=-1,
|
|
178
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blissdata-lima2
|
|
3
|
+
Version: 2.0
|
|
4
|
+
Summary: Lima2 plugin for blissdata
|
|
5
|
+
Maintainer: BCU (ESRF)
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: blissdata,lima2
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE.md
|
|
13
|
+
Requires-Dist: blissdata~=2.3
|
|
14
|
+
Requires-Dist: lima2-client~=4.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: black; extra == "dev"
|
|
17
|
+
Requires-Dist: flake8; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-redis; extra == "dev"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
## blissdata-lima2
|
|
23
|
+
|
|
24
|
+
Lima2 plugin for blissdata.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/blissdata_lima2/__init__.py
|
|
5
|
+
src/blissdata_lima2/stream.py
|
|
6
|
+
src/blissdata_lima2.egg-info/PKG-INFO
|
|
7
|
+
src/blissdata_lima2.egg-info/SOURCES.txt
|
|
8
|
+
src/blissdata_lima2.egg-info/dependency_links.txt
|
|
9
|
+
src/blissdata_lima2.egg-info/entry_points.txt
|
|
10
|
+
src/blissdata_lima2.egg-info/requires.txt
|
|
11
|
+
src/blissdata_lima2.egg-info/top_level.txt
|
|
12
|
+
src/blissdata_lima2/tests/__init__.py
|
|
13
|
+
src/blissdata_lima2/tests/conftest.py
|
|
14
|
+
src/blissdata_lima2/tests/test_lima2_stream.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blissdata_lima2
|