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.
Files changed (104) hide show
  1. {blissdata-2.2.0/src/blissdata.egg-info → blissdata-2.4.0}/PKG-INFO +5 -6
  2. {blissdata-2.2.0 → blissdata-2.4.0}/pyproject.toml +5 -9
  3. blissdata-2.4.0/src/blissdata/__init__.py +4 -0
  4. blissdata-2.4.0/src/blissdata/deprecation.py +12 -0
  5. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/h5_scan_map.py +1 -1
  6. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/redis_hdf5.py +2 -2
  7. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/hdf5.py +12 -1
  8. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/__init__.py +1 -0
  9. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/data_items.py +74 -9
  10. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/lima/client.py +2 -5
  11. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/lima/image_utils.py +2 -2
  12. blissdata-2.4.0/src/blissdata/redis_engine/encoding/json.py +11 -0
  13. blissdata-2.4.0/src/blissdata/redis_engine/encoding/numeric.py +11 -0
  14. blissdata-2.4.0/src/blissdata/redis_engine/event_stream.py +11 -0
  15. blissdata-2.4.0/src/blissdata/redis_engine/exceptions.py +12 -0
  16. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/memory_tracker.py +13 -8
  17. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/scan.py +4 -6
  18. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/store.py +3 -3
  19. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/scan.py +5 -1
  20. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/scan_info_display.py +0 -1
  21. blissdata-2.2.0/src/blissdata/streams/base.py → blissdata-2.4.0/src/blissdata/streams/__init__.py +12 -157
  22. blissdata-2.4.0/src/blissdata/streams/base.py +39 -0
  23. blissdata-2.4.0/src/blissdata/streams/default.py +160 -0
  24. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/event_stream.py +1 -1
  25. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/hdf5_fallback/stream.py +3 -2
  26. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/lima/stream.py +16 -6
  27. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/scan_sequence/stream.py +3 -3
  28. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_files.py +2 -2
  29. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/conftest.py +1 -2
  30. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_dynamic_files.py +89 -0
  31. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_redis_files.py +7 -1
  32. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5map/test_data_items.py +91 -1
  33. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/lima/test_image_utils.py +2 -2
  34. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_cursor.py +4 -3
  35. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_memory_tracker.py +13 -7
  36. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_scan.py +3 -6
  37. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/test_search.py +1 -1
  38. {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/encoding/test_json.py +1 -1
  39. {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/encoding/test_numeric.py +1 -1
  40. {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
  41. {blissdata-2.2.0/src/blissdata/tests/plugin_streams → blissdata-2.4.0/src/blissdata/tests/streams/plugins}/test_plugin_base.py +3 -4
  42. {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams}/test_event_stream.py +3 -3
  43. {blissdata-2.2.0 → blissdata-2.4.0/src/blissdata.egg-info}/PKG-INFO +5 -6
  44. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/SOURCES.txt +15 -11
  45. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/entry_points.txt +0 -1
  46. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/requires.txt +4 -5
  47. blissdata-2.2.0/src/blissdata/streams/lima2/__init__.py +0 -4
  48. blissdata-2.2.0/src/blissdata/streams/lima2/stream.py +0 -266
  49. blissdata-2.2.0/src/blissdata/tests/plugin_streams/test_lima2_stream.py +0 -168
  50. blissdata-2.2.0/src/blissdata/tests/redis_engine/encoding/__init__.py +0 -0
  51. {blissdata-2.2.0 → blissdata-2.4.0}/LICENSE +0 -0
  52. {blissdata-2.2.0 → blissdata-2.4.0}/README.md +0 -0
  53. {blissdata-2.2.0 → blissdata-2.4.0}/setup.cfg +0 -0
  54. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/__init__.py +0 -0
  55. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/_base.py +0 -0
  56. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/_utils.py +0 -0
  57. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/config.py +0 -0
  58. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/data.py +0 -0
  59. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/beacon/files.py +0 -0
  60. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata}/exceptions.py +0 -0
  61. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/__init__.py +0 -0
  62. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/abstract.py +0 -0
  63. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/dynamic_hdf5.py +0 -0
  64. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/file_arguments.py +0 -0
  65. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/static_hdf5.py +0 -0
  66. {blissdata-2.2.0/src/blissdata → blissdata-2.4.0/src/blissdata/h5api/utils}/__init__.py +0 -0
  67. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/bliss.py +0 -0
  68. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/hdf5_retry.py +0 -0
  69. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/lima.py +0 -0
  70. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5api/utils/types.py +0 -0
  71. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/h5map/hdf5_items.py +0 -0
  72. {blissdata-2.2.0/src/blissdata/h5api/utils → blissdata-2.4.0/src/blissdata/lima}/__init__.py +0 -0
  73. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/py.typed +0 -0
  74. {blissdata-2.2.0/src/blissdata/lima → blissdata-2.4.0/src/blissdata/redis_engine}/__init__.py +0 -0
  75. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/redis_engine/encoding}/__init__.py +0 -0
  76. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/redis_engine/identities.py +0 -0
  77. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/__init__.py +0 -0
  78. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/schemas/scan_info.py +0 -0
  79. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/__init__.py +0 -0
  80. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/json.py +0 -0
  81. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/encoding/numeric.py +0 -0
  82. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/hdf5_fallback/__init__.py +0 -0
  83. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/lima/__init__.py +0 -0
  84. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/streams/scan_sequence/__init__.py +0 -0
  85. {blissdata-2.2.0/src/blissdata/redis_engine → blissdata-2.4.0/src/blissdata/streams}/sink.py +0 -0
  86. {blissdata-2.2.0/src/blissdata/streams → blissdata-2.4.0/src/blissdata/tests}/__init__.py +0 -0
  87. {blissdata-2.2.0/src/blissdata/tests → blissdata-2.4.0/src/blissdata/tests/beacon}/__init__.py +0 -0
  88. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_config.py +0 -0
  89. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/beacon/test_data.py +0 -0
  90. {blissdata-2.2.0/src/blissdata/tests/beacon → blissdata-2.4.0/src/blissdata/tests/h5api}/__init__.py +0 -0
  91. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/scanner.py +0 -0
  92. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_h5api_lima_utils.py +0 -0
  93. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_hdf5_handlers.py +0 -0
  94. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5api/test_static_files.py +0 -0
  95. {blissdata-2.2.0/src/blissdata/tests/h5api → blissdata-2.4.0/src/blissdata/tests/h5map}/__init__.py +0 -0
  96. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/h5map/test_hdf5_items.py +0 -0
  97. {blissdata-2.2.0/src/blissdata/tests/h5map → blissdata-2.4.0/src/blissdata/tests/lima}/__init__.py +0 -0
  98. {blissdata-2.2.0/src/blissdata/tests/lima → blissdata-2.4.0/src/blissdata/tests/redis_engine}/__init__.py +0 -0
  99. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/conftest.py +0 -0
  100. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata/tests/redis_engine/utils.py +0 -0
  101. {blissdata-2.2.0/src/blissdata/tests/plugin_streams → blissdata-2.4.0/src/blissdata/tests/streams/encoding}/__init__.py +0 -0
  102. {blissdata-2.2.0/src/blissdata/tests/redis_engine → blissdata-2.4.0/src/blissdata/tests/streams/plugins}/__init__.py +0 -0
  103. {blissdata-2.2.0 → blissdata-2.4.0}/src/blissdata.egg-info/dependency_links.txt +0 -0
  104. {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.2.0
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.3.0
22
+ Requires-Dist: pydantic>=2.9
23
23
  Requires-Dist: redis>=4.6.0
24
- Requires-Dist: redis-om<1.0,>=0.3.1
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[tango]; extra == "lima2"
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==22.3; extra == "dev"
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.2.0"
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.3.0", # (not a direct dep) redis-om 0.3.2 doesn't support lower versions
22
+ "pydantic>=2.9",
23
23
  "redis>=4.6.0",
24
- "redis-om>=0.3.1,<1.0",
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==22.3",
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,4 @@
1
+ from .redis_engine.store import DataStore # noqa F401
2
+ from .redis_engine.scan import Scan, ScanState # noqa F401
3
+
4
+ __all__ = ["DataStore", "Scan", "ScanState"]
@@ -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.redis_engine.scan import ScanState
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.redis_engine.store import DataStore
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 _key_does_not_exist(e):
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)
@@ -4,6 +4,7 @@ from .data_items import ( # noqa: F401
4
4
  InlineRaw,
5
5
  Inline,
6
6
  InlineBase64,
7
+ Compound,
7
8
  Stream,
8
9
  )
9
10
  from .hdf5_items import ( # noqa: F401
@@ -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
- return np.vectorize(lambda x: x.encode(), otypes="O")(value)
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.redis_engine.exceptions import (
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 .store import DataStore
17
- from .scan import Scan
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
- if streams_ids:
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.base import (
26
- BaseStream,
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 .redis_engine.scan import Scan # noqa: F401
7
+ from .deprecation import warn_deprecated
8
+
9
+ warn_deprecated(__name__, "blissdata", "3")
10
+
11
+ from . import Scan # noqa: F401, E402
@@ -7,7 +7,6 @@
7
7
  import typing
8
8
  import typing_extensions
9
9
 
10
-
11
10
  ChannelRef = str
12
11
  """Identify a channel from the `scan_info["channels"]` field"""
13
12
 
@@ -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.redis_engine.event_stream import EventStream
12
- from blissdata.redis_engine.exceptions import (
13
- EndOfStream,
14
- IndexNoMoreThereError,
15
- UnknownEncodingError,
16
- MissingPluginException,
17
- EmptyViewException,
18
- )
19
- from blissdata.redis_engine.encoding import StreamEncoder
20
- from blissdata.redis_engine.encoding.numeric import NumericStreamEncoder
21
- from blissdata.redis_engine.encoding.json import JsonStreamEncoder
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)