blissdata 2.2.0__tar.gz → 2.4.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-2.2.0/src/blissdata.egg-info → blissdata-2.4.0}/PKG-INFO +5 -6
- {blissdata-2.2.0 → blissdata-2.4.0}/pyproject.toml +5 -9
- blissdata-2.4.0/src/blissdata/__init__.py +4 -0
- blissdata-2.4.0/src/blissdata/deprecation.py +12 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/h5_scan_map.py +1 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/redis_hdf5.py +2 -2
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/hdf5.py +12 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/__init__.py +1 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/data_items.py +74 -9
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/lima/client.py +2 -5
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/lima/image_utils.py +2 -2
- blissdata-2.4.0/src/blissdata/redis_engine/encoding/json.py +11 -0
- blissdata-2.4.0/src/blissdata/redis_engine/encoding/numeric.py +11 -0
- blissdata-2.4.0/src/blissdata/redis_engine/event_stream.py +11 -0
- blissdata-2.4.0/src/blissdata/redis_engine/exceptions.py +12 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/memory_tracker.py +13 -8
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/scan.py +4 -6
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/store.py +3 -3
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/scan.py +5 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/scan_info_display.py +0 -1
- blissdata-2.2.0/src/blissdata/streams/base.py → blissdata-2.4.0/src/blissdata/streams/__init__.py +12 -157
- blissdata-2.4.0/src/blissdata/streams/base.py +39 -0
- blissdata-2.4.0/src/blissdata/streams/default.py +160 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/event_stream.py +1 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/hdf5_fallback/stream.py +3 -2
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/lima/stream.py +16 -6
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/scan_sequence/stream.py +3 -3
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_files.py +2 -2
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/conftest.py +1 -2
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_dynamic_files.py +89 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_redis_files.py +7 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5map/test_data_items.py +91 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/lima/test_image_utils.py +2 -2
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_cursor.py +4 -3
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_memory_tracker.py +13 -7
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_scan.py +3 -6
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_search.py +1 -1
- {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/encoding/test_json.py +1 -1
- {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/encoding/test_numeric.py +1 -1
- {blissdata-2.2.0/src/blissdata/tests/plugin_streams → blissdata-2.4.0/src/blissdata/tests/streams/plugins}/test_file_backed_stream.py +4 -4
- {blissdata-2.2.0/src/blissdata/tests/plugin_streams → blissdata-2.4.0/src/blissdata/tests/streams/plugins}/test_plugin_base.py +3 -4
- {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/test_event_stream.py +3 -3
- {blissdata-2.2.0 → blissdata-2.4.0/src/blissdata.egg-info}/PKG-INFO +5 -6
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/SOURCES.txt +15 -11
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/entry_points.txt +0 -1
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/requires.txt +4 -5
- blissdata-2.2.0/src/blissdata/streams/lima2/__init__.py +0 -4
- blissdata-2.2.0/src/blissdata/streams/lima2/stream.py +0 -266
- blissdata-2.2.0/src/blissdata/tests/plugin_streams/test_lima2_stream.py +0 -168
- blissdata-2.2.0/src/blissdata/tests/redis_engine/encoding/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/LICENSE +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/README.md +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/setup.cfg +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/_base.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/_utils.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/config.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/data.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/files.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata}/exceptions.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/abstract.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/dynamic_hdf5.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/file_arguments.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/static_hdf5.py +0 -0
- {blissdata-2.2.0/src/blissdata → blissdata-2.4.0/src/blissdata/h5api/utils}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/bliss.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/hdf5_retry.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/lima.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/types.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/hdf5_items.py +0 -0
- {blissdata-2.2.0/src/blissdata/h5api/utils → blissdata-2.4.0/src/blissdata/lima}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/py.typed +0 -0
- {blissdata-2.2.0/src/blissdata/lima → blissdata-2.4.0/src/blissdata/redis_engine}/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/redis_engine/encoding}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/identities.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/scan_info.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/json.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/numeric.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/hdf5_fallback/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/lima/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/scan_sequence/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/sink.py +0 -0
- {blissdata-2.2.0/src/blissdata/streams → blissdata-2.4.0/src/blissdata/tests}/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests → blissdata-2.4.0/src/blissdata/tests/beacon}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_config.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_data.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/beacon → blissdata-2.4.0/src/blissdata/tests/h5api}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/scanner.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_h5api_lima_utils.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_hdf5_handlers.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_static_files.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/h5api → blissdata-2.4.0/src/blissdata/tests/h5map}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5map/test_hdf5_items.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/h5map → blissdata-2.4.0/src/blissdata/tests/lima}/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/lima → blissdata-2.4.0/src/blissdata/tests/redis_engine}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/conftest.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/utils.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/plugin_streams → blissdata-2.4.0/src/blissdata/tests/streams/encoding}/__init__.py +0 -0
- {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams/plugins}/__init__.py +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/dependency_links.txt +0 -0
- {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blissdata
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Bliss data streaming client
|
|
5
5
|
Maintainer: BCU (ESRF)
|
|
6
6
|
License-Expression: LGPL-3.0-or-later
|
|
@@ -19,15 +19,14 @@ Requires-Dist: h5py>=2.9
|
|
|
19
19
|
Requires-Dist: msgpack
|
|
20
20
|
Requires-Dist: numpy>=1.20
|
|
21
21
|
Requires-Dist: numpydantic<1.6.10
|
|
22
|
-
Requires-Dist: pydantic>=2.
|
|
22
|
+
Requires-Dist: pydantic>=2.9
|
|
23
23
|
Requires-Dist: redis>=4.6.0
|
|
24
|
-
Requires-Dist: redis-om
|
|
24
|
+
Requires-Dist: redis-om~=1.0
|
|
25
25
|
Requires-Dist: silx>=1.1.0
|
|
26
26
|
Provides-Extra: tango
|
|
27
27
|
Requires-Dist: pytango>=9.3.1; extra == "tango"
|
|
28
28
|
Provides-Extra: lima2
|
|
29
|
-
Requires-Dist: blissdata
|
|
30
|
-
Requires-Dist: lima2-client~=4.0; extra == "lima2"
|
|
29
|
+
Requires-Dist: blissdata-lima2>=2.0; extra == "lima2"
|
|
31
30
|
Provides-Extra: test
|
|
32
31
|
Requires-Dist: gevent>=20.9.0; extra == "test"
|
|
33
32
|
Requires-Dist: pytest>=7; extra == "test"
|
|
@@ -37,7 +36,7 @@ Requires-Dist: pytest-timeout; extra == "test"
|
|
|
37
36
|
Requires-Dist: packaging; extra == "test"
|
|
38
37
|
Provides-Extra: dev
|
|
39
38
|
Requires-Dist: blissdata[test]; extra == "dev"
|
|
40
|
-
Requires-Dist: black
|
|
39
|
+
Requires-Dist: black; extra == "dev"
|
|
41
40
|
Requires-Dist: flake8>=4; extra == "dev"
|
|
42
41
|
Requires-Dist: flake8-pep585; extra == "dev"
|
|
43
42
|
Requires-Dist: mkdocs; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "blissdata"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.4.0"
|
|
4
4
|
description = "Bliss data streaming client"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "LGPL-3.0-or-later"
|
|
@@ -19,9 +19,9 @@ dependencies = [
|
|
|
19
19
|
"msgpack",
|
|
20
20
|
"numpy>=1.20",
|
|
21
21
|
"numpydantic<1.6.10",
|
|
22
|
-
"pydantic>=2.
|
|
22
|
+
"pydantic>=2.9",
|
|
23
23
|
"redis>=4.6.0",
|
|
24
|
-
"redis-om
|
|
24
|
+
"redis-om~=1.0",
|
|
25
25
|
"silx>=1.1.0",
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -32,10 +32,7 @@ Changes = "https://gitlab.esrf.fr/bliss/blissdata/-/blob/master/CHANGELOG.md"
|
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
34
|
tango = ["pytango>=9.3.1"]
|
|
35
|
-
lima2 = [
|
|
36
|
-
"blissdata[tango]",
|
|
37
|
-
"lima2-client~=4.0"
|
|
38
|
-
]
|
|
35
|
+
lima2 = ["blissdata-lima2>=2.0"]
|
|
39
36
|
test = [
|
|
40
37
|
"gevent>=20.9.0",
|
|
41
38
|
"pytest>=7",
|
|
@@ -46,7 +43,7 @@ test = [
|
|
|
46
43
|
]
|
|
47
44
|
dev = [
|
|
48
45
|
"blissdata[test]",
|
|
49
|
-
"black
|
|
46
|
+
"black",
|
|
50
47
|
"flake8>=4",
|
|
51
48
|
"flake8-pep585",
|
|
52
49
|
"mkdocs",
|
|
@@ -62,7 +59,6 @@ memory_tracker = "blissdata.redis_engine.memory_tracker:cli"
|
|
|
62
59
|
hdf5_fallback = "blissdata.streams.hdf5_fallback"
|
|
63
60
|
scan_sequence = "blissdata.streams.scan_sequence"
|
|
64
61
|
lima = "blissdata.streams.lima"
|
|
65
|
-
lima2 = "blissdata.streams.lima2"
|
|
66
62
|
|
|
67
63
|
[build-system]
|
|
68
64
|
requires = ["setuptools"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from warnings import warn
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def warn_deprecated(old_name, new_name, removal_version=None):
|
|
5
|
+
msg = f"'{old_name}' is deprecated, use '{new_name}' instead."
|
|
6
|
+
if removal_version:
|
|
7
|
+
msg += f" It will be removed in version {removal_version}."
|
|
8
|
+
warn(
|
|
9
|
+
msg,
|
|
10
|
+
FutureWarning,
|
|
11
|
+
stacklevel=3,
|
|
12
|
+
)
|
|
@@ -2,7 +2,7 @@ import time
|
|
|
2
2
|
import logging
|
|
3
3
|
from collections.abc import Mapping
|
|
4
4
|
from pathlib import PurePosixPath as Path
|
|
5
|
-
from blissdata
|
|
5
|
+
from blissdata import ScanState
|
|
6
6
|
from blissdata.h5map import HDF5ItemType, Group
|
|
7
7
|
|
|
8
8
|
_logger = logging.getLogger(__name__)
|
|
@@ -7,9 +7,9 @@ from abc import ABC, abstractmethod
|
|
|
7
7
|
from pathlib import PurePosixPath as Path
|
|
8
8
|
|
|
9
9
|
import blissdata.h5map as h5m
|
|
10
|
+
from blissdata import DataStore
|
|
10
11
|
from blissdata.beacon.data import BeaconData
|
|
11
|
-
from blissdata.
|
|
12
|
-
from blissdata.redis_engine.exceptions import EndOfStream, IndexNotYetThereError
|
|
12
|
+
from blissdata.exceptions import EndOfStream, IndexNotYetThereError
|
|
13
13
|
from blissdata.h5api import abstract
|
|
14
14
|
from blissdata.h5api.h5_scan_map import H5ScanMap
|
|
15
15
|
|
|
@@ -251,7 +251,7 @@ class DynamicHDF5Handler:
|
|
|
251
251
|
try:
|
|
252
252
|
return item.attrs[key] # try one more time
|
|
253
253
|
except KeyError as e:
|
|
254
|
-
if not
|
|
254
|
+
if not _attribute_does_not_exist(e):
|
|
255
255
|
raise
|
|
256
256
|
# attribute will never be created
|
|
257
257
|
raise RetryTimeoutError(str(e)) from e
|
|
@@ -366,3 +366,14 @@ def _key_does_not_exist(exc: KeyError) -> bool:
|
|
|
366
366
|
|
|
367
367
|
"""
|
|
368
368
|
return "doesn't exist" in str(exc)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _attribute_does_not_exist(exc: KeyError) -> bool:
|
|
372
|
+
"""
|
|
373
|
+
A real KeyError (meaning that the attribute does not exist) gives the following error message:
|
|
374
|
+
|
|
375
|
+
.. code
|
|
376
|
+
|
|
377
|
+
KeyError: "Unable to synchronously open attribute (can't locate attribute: 'myattr')"
|
|
378
|
+
"""
|
|
379
|
+
return "can't locate attribute" in str(exc)
|
|
@@ -6,6 +6,7 @@ from pydantic import (
|
|
|
6
6
|
Base64Bytes,
|
|
7
7
|
Field,
|
|
8
8
|
field_validator,
|
|
9
|
+
model_validator,
|
|
9
10
|
model_serializer,
|
|
10
11
|
Discriminator,
|
|
11
12
|
Tag,
|
|
@@ -64,7 +65,13 @@ class InlineRaw(RootModel):
|
|
|
64
65
|
def decode(self):
|
|
65
66
|
value = self.model_dump()
|
|
66
67
|
if value.dtype.kind in "UT":
|
|
67
|
-
|
|
68
|
+
# Make strings similar to those read back from h5py:
|
|
69
|
+
# - Convert to "object" dtype
|
|
70
|
+
# - Tell h5py that "object" is variable length string using
|
|
71
|
+
# metadata, so writing doesn't fail even in a compound dataset.
|
|
72
|
+
return np.vectorize(
|
|
73
|
+
lambda x: x.encode(), otypes=[np.dtype("O", metadata={"vlen": str})]
|
|
74
|
+
)(value)
|
|
68
75
|
else:
|
|
69
76
|
return value
|
|
70
77
|
|
|
@@ -155,14 +162,6 @@ class InlineBase64(BaseModel):
|
|
|
155
162
|
return value
|
|
156
163
|
|
|
157
164
|
|
|
158
|
-
class Stream(BaseModel):
|
|
159
|
-
type: Literal["stream"] = "stream"
|
|
160
|
-
stream: str
|
|
161
|
-
|
|
162
|
-
def decode(self):
|
|
163
|
-
raise TypeError("Stream model is just a reference, can't use decode()")
|
|
164
|
-
|
|
165
|
-
|
|
166
165
|
def get_discriminator_value(v: Any) -> str:
|
|
167
166
|
if isinstance(v, dict):
|
|
168
167
|
return v.get("type")
|
|
@@ -171,12 +170,78 @@ def get_discriminator_value(v: Any) -> str:
|
|
|
171
170
|
return "raw"
|
|
172
171
|
|
|
173
172
|
|
|
173
|
+
class Compound(BaseModel):
|
|
174
|
+
"""Composite model to combine multiple DataItems as a structured array.
|
|
175
|
+
Unlike numpy structured arrays where data is a list of heterogeneous tuples,
|
|
176
|
+
here all elements of one dtype are stored together. This way existing
|
|
177
|
+
DataItem models can be reused as columns.
|
|
178
|
+
|
|
179
|
+
Validation ensures:
|
|
180
|
+
- Column names uniqueness
|
|
181
|
+
- Shape consistency between columns
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
type: Literal["compound"] = "compound"
|
|
185
|
+
columns: list[
|
|
186
|
+
tuple[
|
|
187
|
+
str,
|
|
188
|
+
Annotated[
|
|
189
|
+
(
|
|
190
|
+
Annotated[Inline, Tag("inline")]
|
|
191
|
+
| Annotated[InlineBase64, Tag("inline_b64")]
|
|
192
|
+
| Annotated[InlineRaw, Tag("raw")]
|
|
193
|
+
| Annotated["Compound", Tag("compound")]
|
|
194
|
+
# Note: Stream can't be part of a compound object
|
|
195
|
+
),
|
|
196
|
+
Discriminator(get_discriminator_value),
|
|
197
|
+
],
|
|
198
|
+
]
|
|
199
|
+
] = Field(min_length=1)
|
|
200
|
+
# Reusing DataItemType simplifies this model as it does not redefine
|
|
201
|
+
# handling of dtype, shape or base64 version of the values.
|
|
202
|
+
# Therefore the representation is the transposed of numpy one, which can be
|
|
203
|
+
# harder to read.
|
|
204
|
+
|
|
205
|
+
def decode(self):
|
|
206
|
+
decoded_columns = [(name, value.decode()) for name, value in self.columns]
|
|
207
|
+
rec = np.rec.fromarrays(
|
|
208
|
+
[value for _, value in decoded_columns],
|
|
209
|
+
names=[name for name, _ in decoded_columns],
|
|
210
|
+
)
|
|
211
|
+
return rec.view(np.ndarray)
|
|
212
|
+
|
|
213
|
+
@model_validator(mode="after")
|
|
214
|
+
@classmethod
|
|
215
|
+
def verify_names_and_shapes(cls, value):
|
|
216
|
+
"""Ensure uniqueness of names and shape equality between columns"""
|
|
217
|
+
if len(value.columns) != len({name for name, _ in value.columns}):
|
|
218
|
+
raise ValueError("found columns with same name")
|
|
219
|
+
|
|
220
|
+
shape_iterator = iter((col.decode().shape for _, col in value.columns))
|
|
221
|
+
try:
|
|
222
|
+
first_shape = next(shape_iterator)
|
|
223
|
+
except StopIteration:
|
|
224
|
+
return value
|
|
225
|
+
if not all(first_shape == shape for shape in shape_iterator):
|
|
226
|
+
raise ValueError("columns have different shapes")
|
|
227
|
+
return value
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class Stream(BaseModel):
|
|
231
|
+
type: Literal["stream"] = "stream"
|
|
232
|
+
stream: str
|
|
233
|
+
|
|
234
|
+
def decode(self):
|
|
235
|
+
raise TypeError("Stream model is just a reference, can't use decode()")
|
|
236
|
+
|
|
237
|
+
|
|
174
238
|
DataItemType = Annotated[
|
|
175
239
|
(
|
|
176
240
|
Annotated[Inline, Tag("inline")]
|
|
177
241
|
| Annotated[InlineBase64, Tag("inline_b64")]
|
|
178
242
|
| Annotated[Stream, Tag("stream")]
|
|
179
243
|
| Annotated[InlineRaw, Tag("raw")]
|
|
244
|
+
| Annotated[Compound, Tag("compound")]
|
|
180
245
|
),
|
|
181
246
|
Discriminator(get_discriminator_value),
|
|
182
247
|
]
|
|
@@ -5,16 +5,13 @@
|
|
|
5
5
|
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
6
|
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
7
|
import math
|
|
8
|
+
import logging
|
|
8
9
|
import numpy as np
|
|
9
10
|
from packaging.version import Version
|
|
10
11
|
from functools import cached_property
|
|
11
|
-
import logging
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
|
|
14
|
-
from blissdata.
|
|
15
|
-
IndexNoMoreThereError,
|
|
16
|
-
IndexNotYetThereError,
|
|
17
|
-
)
|
|
14
|
+
from blissdata.exceptions import IndexNoMoreThereError, IndexNotYetThereError
|
|
18
15
|
from . import image_utils
|
|
19
16
|
|
|
20
17
|
try:
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
6
|
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
7
|
|
|
8
|
-
"""Utility functions to read Lima images from file or device server.
|
|
9
|
-
|
|
8
|
+
"""Utility functions to read Lima images from file or device server."""
|
|
9
|
+
|
|
10
10
|
import struct
|
|
11
11
|
import numpy
|
|
12
12
|
import typing
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
from blissdata.deprecation import warn_deprecated
|
|
8
|
+
|
|
9
|
+
warn_deprecated(__name__, "blissdata.streams.encoding.json", "3")
|
|
10
|
+
|
|
11
|
+
from blissdata.streams.encoding.json import JsonStreamEncoder # noqa F401, E402
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
from blissdata.deprecation import warn_deprecated
|
|
8
|
+
|
|
9
|
+
warn_deprecated(__name__, "blissdata.streams.encoding.numeric", "3")
|
|
10
|
+
|
|
11
|
+
from blissdata.streams.encoding.numeric import NumericStreamEncoder # noqa F401, E402
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
from blissdata.deprecation import warn_deprecated
|
|
8
|
+
|
|
9
|
+
warn_deprecated(__name__, "blissdata.streams.event_stream", "3")
|
|
10
|
+
|
|
11
|
+
from blissdata.streams.event_stream import EventStream # noqa F401, E402
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
from blissdata.deprecation import warn_deprecated
|
|
9
|
+
|
|
10
|
+
warn_deprecated(__name__, "blissdata.exceptions", "3")
|
|
11
|
+
|
|
12
|
+
from blissdata.exceptions import * # noqa F401, E402
|
|
@@ -13,11 +13,8 @@ import redis
|
|
|
13
13
|
from datetime import timedelta
|
|
14
14
|
from dataclasses import dataclass
|
|
15
15
|
|
|
16
|
-
from
|
|
17
|
-
from .
|
|
18
|
-
from .scan import ScanState
|
|
19
|
-
from .exceptions import NoScanAvailable, ScanNotFoundError, ScanLoadError
|
|
20
|
-
|
|
16
|
+
from blissdata import DataStore, Scan, ScanState
|
|
17
|
+
from blissdata.exceptions import NoScanAvailable, ScanNotFoundError, ScanLoadError
|
|
21
18
|
|
|
22
19
|
memory_tracking_stream = "_MEMORY_TRACKING_"
|
|
23
20
|
SEAL_ID = ("-".join([str(2**64 - 1)] * 2)).encode()
|
|
@@ -37,6 +34,7 @@ class NoFreeableMemoryError(Exception):
|
|
|
37
34
|
class TrackedScan:
|
|
38
35
|
scan: Scan
|
|
39
36
|
last_activity: int
|
|
37
|
+
last_streams_ids: dict[str, int]
|
|
40
38
|
|
|
41
39
|
def __eq__(self, other):
|
|
42
40
|
return self.scan.key == other.scan.key
|
|
@@ -95,7 +93,7 @@ class MemoryTracker:
|
|
|
95
93
|
scan._model.state_stream.key(), count=1
|
|
96
94
|
)
|
|
97
95
|
last_activity = int(raw[0][0].split(b"-")[0]) / 1000
|
|
98
|
-
self._tracked_scans.add(TrackedScan(scan, last_activity))
|
|
96
|
+
self._tracked_scans.add(TrackedScan(scan, last_activity, {}))
|
|
99
97
|
except ScanNotFoundError:
|
|
100
98
|
# scan already deleted from Redis by user, skip it
|
|
101
99
|
pass
|
|
@@ -115,7 +113,7 @@ class MemoryTracker:
|
|
|
115
113
|
try:
|
|
116
114
|
scan = self._data_store.load_scan(scan_key)
|
|
117
115
|
last_activity = int(since.split("-")[0]) / 1000
|
|
118
|
-
self._tracked_scans.add(TrackedScan(scan, last_activity))
|
|
116
|
+
self._tracked_scans.add(TrackedScan(scan, last_activity, {}))
|
|
119
117
|
self._since = since
|
|
120
118
|
logging.info(f"Start tracking {scan.key}")
|
|
121
119
|
except ScanNotFoundError:
|
|
@@ -137,7 +135,14 @@ class MemoryTracker:
|
|
|
137
135
|
|
|
138
136
|
if track.scan.state >= ScanState.PREPARED:
|
|
139
137
|
streams_ids = self._data_streams_ids(track.scan)
|
|
140
|
-
|
|
138
|
+
stream_activity = {
|
|
139
|
+
key: idx
|
|
140
|
+
for key, idx in streams_ids.items()
|
|
141
|
+
if key not in track.last_streams_ids
|
|
142
|
+
or track.last_streams_ids[key] != idx
|
|
143
|
+
}
|
|
144
|
+
if stream_activity:
|
|
145
|
+
track.last_streams_ids = streams_ids
|
|
141
146
|
track.last_activity, _ = self._data_store._redis.time()
|
|
142
147
|
all_data_streams_ids.update(streams_ids)
|
|
143
148
|
|
|
@@ -11,21 +11,19 @@ import math
|
|
|
11
11
|
import logging
|
|
12
12
|
import numpy as np
|
|
13
13
|
from functools import wraps
|
|
14
|
-
|
|
15
14
|
from pydantic import ValidationError
|
|
16
15
|
from importlib.metadata import entry_points
|
|
17
16
|
|
|
18
|
-
from .event_stream import EventStream
|
|
19
|
-
from .exceptions import (
|
|
17
|
+
from blissdata.streams.event_stream import EventStream
|
|
18
|
+
from blissdata.exceptions import (
|
|
20
19
|
UnauthorizeStateTransition,
|
|
21
20
|
ScanNotFoundError,
|
|
22
21
|
ScanValidationError,
|
|
23
22
|
NoWritePermission,
|
|
24
23
|
)
|
|
25
|
-
from blissdata.streams
|
|
26
|
-
|
|
24
|
+
from blissdata.streams import BaseStream, StreamDefinition
|
|
25
|
+
from blissdata.streams.default import (
|
|
27
26
|
Stream,
|
|
28
|
-
StreamDefinition,
|
|
29
27
|
BrokenStream,
|
|
30
28
|
BrokenPluginStream,
|
|
31
29
|
MissingPluginStream,
|
|
@@ -13,8 +13,8 @@ from redis.commands.search.query import Query
|
|
|
13
13
|
from redis.commands.search.querystring import querystring, tags
|
|
14
14
|
from redis_om import HashModel, JsonModel, Migrator
|
|
15
15
|
|
|
16
|
+
from blissdata.exceptions import NoScanAvailable, ScanNotFoundError
|
|
16
17
|
from .scan import Scan, scan_creation_stream, ScanState
|
|
17
|
-
from .exceptions import NoScanAvailable, ScanNotFoundError
|
|
18
18
|
from .identities import ESRFIdentityModel
|
|
19
19
|
|
|
20
20
|
_PROTOCOL_KEY = "_PROTOCOL_VERSION_"
|
|
@@ -43,7 +43,7 @@ class DataStore:
|
|
|
43
43
|
close_func = type(redis_pool).disconnect
|
|
44
44
|
weakref.finalize(self, close_func, redis_pool)
|
|
45
45
|
|
|
46
|
-
class StreamModel(HashModel):
|
|
46
|
+
class StreamModel(HashModel, index=True):
|
|
47
47
|
"""Actual stream model to be embedded into ScanModel, but not exposed to user directly.
|
|
48
48
|
They are wrapped into Stream objects instead."""
|
|
49
49
|
|
|
@@ -60,7 +60,7 @@ class DataStore:
|
|
|
60
60
|
encoding: dict | str | None = None
|
|
61
61
|
info: dict | str | None = None
|
|
62
62
|
|
|
63
|
-
class ScanModel(JsonModel):
|
|
63
|
+
class ScanModel(JsonModel, index=True):
|
|
64
64
|
class Meta:
|
|
65
65
|
global_key_prefix = identity_model_cls._meta.global_key_prefix
|
|
66
66
|
model_key_prefix = "scan"
|
|
@@ -4,4 +4,8 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
6
|
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
|
-
from .
|
|
7
|
+
from .deprecation import warn_deprecated
|
|
8
|
+
|
|
9
|
+
warn_deprecated(__name__, "blissdata", "3")
|
|
10
|
+
|
|
11
|
+
from . import Scan # noqa: F401, E402
|
blissdata-2.2.0/src/blissdata/streams/base.py → blissdata-2.4.0/src/blissdata/streams/__init__.py
RENAMED
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
6
|
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
|
-
import numpy as np
|
|
8
7
|
from abc import ABC, abstractmethod
|
|
9
8
|
from collections.abc import Mapping, Sequence
|
|
10
9
|
from dataclasses import dataclass
|
|
11
|
-
from blissdata.
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
from blissdata.exceptions import EndOfStream, EmptyViewException
|
|
11
|
+
from .encoding import StreamEncoder
|
|
12
|
+
from .event_stream import EventStream
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"BaseStream",
|
|
16
|
+
"BaseView",
|
|
17
|
+
"Cursor",
|
|
18
|
+
"CursorGroup",
|
|
19
|
+
"EventRange",
|
|
20
|
+
"StreamDefinition",
|
|
21
|
+
]
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@dataclass(slots=True)
|
|
@@ -174,151 +174,6 @@ class BaseView(ABC):
|
|
|
174
174
|
pass
|
|
175
175
|
|
|
176
176
|
|
|
177
|
-
class View(BaseView):
|
|
178
|
-
def __init__(self, event_range: EventRange):
|
|
179
|
-
self._events = event_range
|
|
180
|
-
|
|
181
|
-
@property
|
|
182
|
-
def index(self) -> int:
|
|
183
|
-
return self._events.index
|
|
184
|
-
|
|
185
|
-
def __len__(self) -> int:
|
|
186
|
-
return len(self._events)
|
|
187
|
-
|
|
188
|
-
def __iter__(self):
|
|
189
|
-
yield from self.get_data()
|
|
190
|
-
|
|
191
|
-
def get_data(self, start=None, stop=None):
|
|
192
|
-
trimmed_range = range(len(self))[start:stop]
|
|
193
|
-
offset = self._events.nb_expired
|
|
194
|
-
data_start = trimmed_range.start - offset
|
|
195
|
-
data_stop = trimmed_range.stop - offset
|
|
196
|
-
if data_start < 0:
|
|
197
|
-
raise IndexNoMoreThereError
|
|
198
|
-
else:
|
|
199
|
-
return self._events.data[data_start:data_stop]
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
class Stream(BaseStream):
|
|
203
|
-
def __init__(self, event_stream):
|
|
204
|
-
super().__init__(event_stream)
|
|
205
|
-
if event_stream.encoding["type"] == "numeric":
|
|
206
|
-
self._kind = "array"
|
|
207
|
-
elif event_stream.encoding["type"] == "json":
|
|
208
|
-
self._kind = "json"
|
|
209
|
-
else:
|
|
210
|
-
raise UnknownEncodingError(
|
|
211
|
-
f"Unknow stream encoding {event_stream.encoding}"
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
@staticmethod
|
|
215
|
-
def make_definition(name, dtype, shape=None, info={}) -> StreamDefinition:
|
|
216
|
-
if dtype == "json":
|
|
217
|
-
if shape is None:
|
|
218
|
-
return StreamDefinition(name, info, JsonStreamEncoder())
|
|
219
|
-
else:
|
|
220
|
-
raise ValueError("JSON stream cannot have shape")
|
|
221
|
-
else:
|
|
222
|
-
if shape is None:
|
|
223
|
-
shape = ()
|
|
224
|
-
info = info.copy()
|
|
225
|
-
info["dtype"] = np.dtype(dtype).name
|
|
226
|
-
info["shape"] = shape
|
|
227
|
-
return StreamDefinition(name, info, NumericStreamEncoder(dtype, shape))
|
|
228
|
-
|
|
229
|
-
@property
|
|
230
|
-
def kind(self):
|
|
231
|
-
return self._kind
|
|
232
|
-
|
|
233
|
-
@property
|
|
234
|
-
def plugin(self):
|
|
235
|
-
None
|
|
236
|
-
|
|
237
|
-
@property
|
|
238
|
-
def dtype(self):
|
|
239
|
-
return self.event_stream.dtype
|
|
240
|
-
|
|
241
|
-
@property
|
|
242
|
-
def shape(self):
|
|
243
|
-
return self.event_stream.shape
|
|
244
|
-
|
|
245
|
-
def __len__(self):
|
|
246
|
-
return len(self._event_stream)
|
|
247
|
-
|
|
248
|
-
def __getitem__(self, key):
|
|
249
|
-
return self._event_stream[key]
|
|
250
|
-
|
|
251
|
-
def _need_last_only(self, last_only):
|
|
252
|
-
return last_only
|
|
253
|
-
|
|
254
|
-
def _build_view_from_events(self, index, events: EventRange, last_only):
|
|
255
|
-
# NOTE event_range is never empty
|
|
256
|
-
return View(events)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
class BrokenStream(BaseStream):
|
|
260
|
-
def __init__(self, event_stream, plugin_name, exception, kind):
|
|
261
|
-
self._event_stream = event_stream
|
|
262
|
-
self._plugin_name = plugin_name
|
|
263
|
-
self._exception = exception
|
|
264
|
-
self._kind = kind
|
|
265
|
-
|
|
266
|
-
@property
|
|
267
|
-
def kind(self):
|
|
268
|
-
self._kind
|
|
269
|
-
|
|
270
|
-
@staticmethod
|
|
271
|
-
def make_definition(*args, **kwargs) -> StreamDefinition:
|
|
272
|
-
raise NotImplementedError
|
|
273
|
-
|
|
274
|
-
@property
|
|
275
|
-
def plugin(self):
|
|
276
|
-
return self._plugin_name
|
|
277
|
-
|
|
278
|
-
@property
|
|
279
|
-
def dtype(self):
|
|
280
|
-
raise self._exception
|
|
281
|
-
|
|
282
|
-
@property
|
|
283
|
-
def shape(self):
|
|
284
|
-
raise self._exception
|
|
285
|
-
|
|
286
|
-
def __len__(self):
|
|
287
|
-
raise self._exception
|
|
288
|
-
|
|
289
|
-
def __getitem__(self, key):
|
|
290
|
-
raise self._exception
|
|
291
|
-
|
|
292
|
-
def _need_last_only(self, last_only):
|
|
293
|
-
raise self._exception
|
|
294
|
-
|
|
295
|
-
def _build_view_from_events(
|
|
296
|
-
self, hl_index: int, events: EventRange, last_only: bool
|
|
297
|
-
):
|
|
298
|
-
raise self._exception
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
class BrokenPluginStream(BrokenStream):
|
|
302
|
-
"""A stream class instanciated in place of a plugin, if it can't be
|
|
303
|
-
instantiated. A warning is issued, but the scan object is not affected,
|
|
304
|
-
ensuring memory tracker robustness against plugins."""
|
|
305
|
-
|
|
306
|
-
def __init__(self, event_stream, plugin_name, exception):
|
|
307
|
-
super().__init__(event_stream, plugin_name, exception, "broken_plugin")
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
class MissingPluginStream(BrokenStream):
|
|
311
|
-
"""A stream class instanciated in place of a plugin if not installed."""
|
|
312
|
-
|
|
313
|
-
def __init__(self, event_stream, plugin_name):
|
|
314
|
-
super().__init__(
|
|
315
|
-
event_stream,
|
|
316
|
-
plugin_name,
|
|
317
|
-
MissingPluginException(event_stream.name, plugin_name),
|
|
318
|
-
"missing_plugin",
|
|
319
|
-
)
|
|
320
|
-
|
|
321
|
-
|
|
322
177
|
class Cursor:
|
|
323
178
|
def __init__(self, stream):
|
|
324
179
|
# low level stream (multiplexable events from redis)
|