imas-python 0.1.dev1618__py3-none-any.whl

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.

Potentially problematic release.


This version of imas-python might be problematic. Click here for more details.

Files changed (108) hide show
  1. imas/__init__.py +34 -0
  2. imas/__main__.py +17 -0
  3. imas/_util.py +260 -0
  4. imas/_version.py +16 -0
  5. imas/assets/IDSDef2MDSpreTree.xsl +161 -0
  6. imas/assets/IDS_fake_toplevel.xml +318 -0
  7. imas/assets/IDS_minimal.xml +37 -0
  8. imas/assets/IDS_minimal_2.xml +37 -0
  9. imas/assets/IDS_minimal_struct_array.xml +43 -0
  10. imas/assets/IDS_minimal_types.xml +169 -0
  11. imas/assets/ITER_134173_106_core_profiles.ids +2438 -0
  12. imas/assets/ITER_134173_106_equilibrium.ids +3172 -0
  13. imas/assets/README.md +5 -0
  14. imas/assets/core_profiles.ids +2438 -0
  15. imas/assets/equilibrium.ids +3172 -0
  16. imas/backends/__init__.py +12 -0
  17. imas/backends/db_entry_impl.py +122 -0
  18. imas/backends/imas_core/__init__.py +4 -0
  19. imas/backends/imas_core/al_context.py +391 -0
  20. imas/backends/imas_core/db_entry_al.py +405 -0
  21. imas/backends/imas_core/db_entry_helpers.py +200 -0
  22. imas/backends/imas_core/imas_interface.py +216 -0
  23. imas/backends/imas_core/mdsplus_model.py +367 -0
  24. imas/backends/imas_core/uda_support.py +56 -0
  25. imas/backends/netcdf/__init__.py +4 -0
  26. imas/backends/netcdf/db_entry_nc.py +176 -0
  27. imas/backends/netcdf/ids2nc.py +306 -0
  28. imas/backends/netcdf/nc2ids.py +302 -0
  29. imas/backends/netcdf/nc_metadata.py +357 -0
  30. imas/backends/netcdf/nc_validate.py +53 -0
  31. imas/command/cli.py +239 -0
  32. imas/command/db_analysis.py +290 -0
  33. imas/command/helpers.py +45 -0
  34. imas/command/timer.py +77 -0
  35. imas/db_entry.py +802 -0
  36. imas/dd_helpers.py +300 -0
  37. imas/dd_zip.py +297 -0
  38. imas/exception.py +107 -0
  39. imas/ids_base.py +149 -0
  40. imas/ids_convert.py +920 -0
  41. imas/ids_coordinates.py +400 -0
  42. imas/ids_data_type.py +115 -0
  43. imas/ids_defs.py +171 -0
  44. imas/ids_factory.py +108 -0
  45. imas/ids_identifiers.py +125 -0
  46. imas/ids_metadata.py +307 -0
  47. imas/ids_path.py +254 -0
  48. imas/ids_primitive.py +510 -0
  49. imas/ids_struct_array.py +233 -0
  50. imas/ids_structure.py +331 -0
  51. imas/ids_toplevel.py +381 -0
  52. imas/setup_logging.py +64 -0
  53. imas/test/test_all_dd_versions.py +28 -0
  54. imas/test/test_cli.py +44 -0
  55. imas/test/test_dbentry.py +104 -0
  56. imas/test/test_dd_helpers.py +134 -0
  57. imas/test/test_dd_zip.py +29 -0
  58. imas/test/test_deepcopy.py +25 -0
  59. imas/test/test_exception.py +12 -0
  60. imas/test/test_get_sample.py +388 -0
  61. imas/test/test_hash.py +188 -0
  62. imas/test/test_helpers.py +354 -0
  63. imas/test/test_identifiers.py +105 -0
  64. imas/test/test_ids2nc.py +166 -0
  65. imas/test/test_ids_ascii_data.py +39 -0
  66. imas/test/test_ids_convert.py +415 -0
  67. imas/test/test_ids_coordinates.py +165 -0
  68. imas/test/test_ids_data_type.py +40 -0
  69. imas/test/test_ids_factory.py +35 -0
  70. imas/test/test_ids_metadata.py +66 -0
  71. imas/test/test_ids_mixin.py +20 -0
  72. imas/test/test_ids_path.py +192 -0
  73. imas/test/test_ids_primitive.py +109 -0
  74. imas/test/test_ids_struct_array.py +89 -0
  75. imas/test/test_ids_structure.py +70 -0
  76. imas/test/test_ids_toplevel.py +55 -0
  77. imas/test/test_ids_validate.py +343 -0
  78. imas/test/test_latest_dd_autofill.py +74 -0
  79. imas/test/test_latest_dd_manual.py +38 -0
  80. imas/test/test_latest_dd_resample.py +102 -0
  81. imas/test/test_lazy_loading.py +201 -0
  82. imas/test/test_list_occurrences.py +82 -0
  83. imas/test/test_minimal.py +37 -0
  84. imas/test/test_minimal_conversion.py +44 -0
  85. imas/test/test_minimal_io.py +21 -0
  86. imas/test/test_minimal_struct_array_io.py +60 -0
  87. imas/test/test_minimal_types.py +436 -0
  88. imas/test/test_minimal_types_io.py +70 -0
  89. imas/test/test_minimal_types_io_automatic.py +66 -0
  90. imas/test/test_nbc_change.py +408 -0
  91. imas/test/test_nc_autofill.py +13 -0
  92. imas/test/test_nc_entry.py +28 -0
  93. imas/test/test_nc_metadata.py +153 -0
  94. imas/test/test_nc_validation.py +197 -0
  95. imas/test/test_snippets.py +25 -0
  96. imas/test/test_static_ids.py +39 -0
  97. imas/test/test_str_1d.py +99 -0
  98. imas/test/test_time_slicing.py +182 -0
  99. imas/test/test_uda_support.py +12 -0
  100. imas/test/test_util.py +209 -0
  101. imas/training.py +45 -0
  102. imas/util.py +526 -0
  103. imas_python-0.1.dev1618.dist-info/LICENSE.txt +165 -0
  104. imas_python-0.1.dev1618.dist-info/METADATA +288 -0
  105. imas_python-0.1.dev1618.dist-info/RECORD +108 -0
  106. imas_python-0.1.dev1618.dist-info/WHEEL +5 -0
  107. imas_python-0.1.dev1618.dist-info/entry_points.txt +3 -0
  108. imas_python-0.1.dev1618.dist-info/top_level.txt +1 -0
@@ -0,0 +1,12 @@
1
+ # This file is part of imas-python.
2
+ # You should have received the imas-python LICENSE file with this project.
3
+ """Logic for interacting with all data backends.
4
+
5
+ Currently supported backends are:
6
+
7
+ - ``imas_core``: IMAS Access Layer Core (lowlevel).
8
+
9
+ Interfaces with the AL core provided by Python package ``imas_core`` (available
10
+ since AL5.2). For older versions it falls back to the ``imas`` HLI module, which
11
+ contains the interface to ``imas_core``.
12
+ """
@@ -0,0 +1,122 @@
1
+ # This file is part of imas-python.
2
+ # You should have received the imas-python LICENSE file with this project.
3
+
4
+ from abc import ABC, abstractmethod
5
+ from dataclasses import dataclass
6
+ from typing import Any, List, Optional, Union
7
+
8
+ import numpy
9
+
10
+ from imas.ids_convert import NBCPathMap
11
+ from imas.ids_factory import IDSFactory
12
+ from imas.ids_toplevel import IDSToplevel
13
+
14
+
15
+ @dataclass
16
+ class GetSliceParameters:
17
+ """Helper class to store parameters to get_slice."""
18
+
19
+ time_requested: float
20
+ """See :param:`imas.db_entry.DBEntry.get_slice.time_requested`."""
21
+ interpolation_method: int
22
+ """See :param:`imas.db_entry.DBEntry.get_slice.interpolation_method`."""
23
+
24
+
25
+ @dataclass
26
+ class GetSampleParameters:
27
+ """Helper class to store parameters to get_sample."""
28
+
29
+ tmin: float
30
+ """See :param:`imas.db_entry.DBEntry.get_sample.tmin`."""
31
+ tmax: float
32
+ """See :param:`imas.db_entry.DBEntry.get_sample.tmax`."""
33
+ dtime: Optional[numpy.ndarray]
34
+ """See :param:`imas.db_entry.DBEntry.get_sample.dtime`."""
35
+ interpolation_method: Optional[int]
36
+ """See :param:`imas.db_entry.DBEntry.get_sample.interpolation_method`."""
37
+
38
+
39
+ class DBEntryImpl(ABC):
40
+ """Interface for DBEntry implementations."""
41
+
42
+ @classmethod
43
+ @abstractmethod
44
+ def from_uri(cls, uri: str, mode: str, factory: IDSFactory) -> "DBEntryImpl":
45
+ """Open a datasource by URI."""
46
+
47
+ @classmethod
48
+ def from_pulse_run(
49
+ cls,
50
+ backend_id: int,
51
+ db_name: str,
52
+ pulse: int,
53
+ run: int,
54
+ user_name: Optional[str],
55
+ data_version: Optional[str],
56
+ mode: int,
57
+ options: Any,
58
+ factory: IDSFactory,
59
+ ) -> "DBEntryImpl":
60
+ """Open a datasource with pulse, run and other legacy arguments."""
61
+ raise NotImplementedError() # Only the `imas_core` backend implements this.
62
+
63
+ @abstractmethod
64
+ def close(self, *, erase: bool = False) -> None:
65
+ """Close the data source.
66
+
67
+ Keyword Args:
68
+ erase: The Access Layer allowed a parameter to erase data files when
69
+ closing. This parameter may be ignored when implementing a backend.
70
+ """
71
+
72
+ @abstractmethod
73
+ def get(
74
+ self,
75
+ ids_name: str,
76
+ occurrence: int,
77
+ parameters: Union[None, GetSliceParameters, GetSampleParameters],
78
+ destination: IDSToplevel,
79
+ lazy: bool,
80
+ nbc_map: Optional[NBCPathMap],
81
+ ) -> None:
82
+ """Implement DBEntry.get/get_slice/get_sample. Load data from the data source.
83
+
84
+ Args:
85
+ ids_name: Name of the IDS to load.
86
+ occurrence: Which occurence of the IDS to load.
87
+ parameters: Additional parameters for a get_slice/get_sample call.
88
+ destination: IDS object to store data in.
89
+ lazy: Use lazy loading.
90
+ nbc_map: NBCPathMap to use for implicit conversion. When None, no implicit
91
+ conversion needs to be done.
92
+ """
93
+
94
+ @abstractmethod
95
+ def read_dd_version(self, ids_name: str, occurrence: int) -> str:
96
+ """Read data dictionary version that the requested IDS was stored with.
97
+
98
+ This method should raise a DataEntryException if the specified ids/occurrence is
99
+ not filled.
100
+ """
101
+
102
+ @abstractmethod
103
+ def put(self, ids: IDSToplevel, occurrence: int, is_slice: bool) -> None:
104
+ """Implement DBEntry.put()/put_slice(): store data.
105
+
106
+ Args:
107
+ ids: IDS to store in the data source.
108
+ occurrence: Which occurrence of the IDS to store to.
109
+ is_slice: True: put_slice(), False: put()
110
+ """
111
+
112
+ @abstractmethod
113
+ def access_layer_version(self) -> str:
114
+ """Get the access layer version used to store data."""
115
+
116
+ @abstractmethod
117
+ def delete_data(self, ids_name: str, occurrence: int) -> None:
118
+ """Implement DBEntry.delete_data()"""
119
+
120
+ @abstractmethod
121
+ def list_all_occurrences(self, ids_name: str) -> List[int]:
122
+ """Implement DBEntry.list_all_occurrences()"""
@@ -0,0 +1,4 @@
1
+ # This file is part of imas-python.
2
+ # You should have received the imas-python LICENSE file with this project.
3
+ """Subpackage implementing data access through the IMAS Access Layer Core.
4
+ """
@@ -0,0 +1,391 @@
1
+ # This file is part of imas-python.
2
+ # You should have received the imas-python LICENSE file with this project.
3
+ """Object-oriented interface to the IMAS lowlevel.
4
+ """
5
+
6
+ import logging
7
+ import weakref
8
+ from contextlib import contextmanager
9
+ from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Tuple
10
+
11
+ import numpy
12
+
13
+ from imas.backends.imas_core.imas_interface import ll_interface
14
+ from imas.exception import LowlevelError
15
+ from imas.ids_defs import (
16
+ CLOSEST_INTERP,
17
+ LINEAR_INTERP,
18
+ PREVIOUS_INTERP,
19
+ UNDEFINED_INTERP,
20
+ )
21
+
22
+ INTERP_MODES = (
23
+ CLOSEST_INTERP,
24
+ LINEAR_INTERP,
25
+ PREVIOUS_INTERP,
26
+ UNDEFINED_INTERP,
27
+ )
28
+
29
+ if TYPE_CHECKING:
30
+ from imas.backends.imas_core.db_entry_al import ALDBEntryImpl
31
+ from imas.ids_convert import NBCPathMap
32
+
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class ALContext:
38
+ """Helper class that wraps Access Layer contexts.
39
+
40
+ Provides:
41
+
42
+ - Object oriented wrappers around AL lowlevel methods which require a context
43
+ - Context managers for creating and automatically ending AL actions
44
+ """
45
+
46
+ __slots__ = ["ctx", "__weakref__"]
47
+
48
+ def __init__(self, ctx: int) -> None:
49
+ """Construct a new ALContext object
50
+
51
+ Args:
52
+ ctx: Context identifier returned by the AL
53
+ """
54
+ self.ctx = ctx
55
+ """Context identifier"""
56
+
57
+ def __enter__(self) -> "ALContext":
58
+ return self
59
+
60
+ def __exit__(self, exc_type, exc_value, traceback) -> None:
61
+ ll_interface.end_action(self.ctx)
62
+
63
+ def global_action(self, path: str, rwmode: int) -> "ALContext":
64
+ """Begin a new global action for use in a ``with`` context.
65
+
66
+ Args:
67
+ path: access layer path for this global action: ``<idsname>[/<occurrence>]``
68
+ rwmode: read-only or read-write operation mode: ``READ_OP``/``WRITE_OP``
69
+
70
+ Returns:
71
+ The created context.
72
+ """
73
+ status, ctx = ll_interface.begin_global_action(self.ctx, path, rwmode)
74
+ if status != 0:
75
+ raise LowlevelError("global_action", status)
76
+ return ALContext(ctx)
77
+
78
+ def slice_action(
79
+ self, path: str, rwmode: int, time_requested: float, interpolation_method: int
80
+ ) -> "ALContext":
81
+ """Begin a new slice action for use in a ``with`` context.
82
+
83
+ Args:
84
+ path: access layer path for this global action: ``<idsname>[/<occurrence>]``
85
+ rwmode: read-only or read-write operation mode: ``READ_OP``/``WRITE_OP``
86
+ time_requested: time-point requested. Use ``UNDEFINED_TIME`` for put_slice.
87
+ interpolation_method: interpolation method to use: ``CLOSEST_INTERP``,
88
+ ``LINEAR_INTERP`` or ``PREVIOUS_INTERP`` for get_slice;
89
+ ``UNDEFINED_INTERP`` for put_slice.
90
+
91
+ Returns:
92
+ The created context.
93
+ """
94
+ if interpolation_method not in INTERP_MODES:
95
+ raise ValueError(
96
+ "get_slice called with unexpected interpolation method: "
97
+ f"{interpolation_method}"
98
+ )
99
+ status, ctx = ll_interface.begin_slice_action(
100
+ self.ctx,
101
+ path,
102
+ rwmode,
103
+ time_requested,
104
+ interpolation_method,
105
+ )
106
+ if status != 0:
107
+ raise LowlevelError("slice_action", status)
108
+ return ALContext(ctx)
109
+
110
+ def timerange_action(
111
+ self,
112
+ path: str,
113
+ rwmode: int,
114
+ tmin: float,
115
+ tmax: float,
116
+ dtime: Optional[numpy.ndarray],
117
+ interpolation_method: int,
118
+ ) -> "ALContext":
119
+ """Begin a new timerange action for use in a ``with`` context."""
120
+ ctx = ll_interface.begin_timerange_action(
121
+ self.ctx, path, rwmode, tmin, tmax, dtime, interpolation_method
122
+ )
123
+ return ALContext(ctx)
124
+
125
+ def arraystruct_action(
126
+ self, path: str, timebase: str, size: int
127
+ ) -> "ALArrayStructContext":
128
+ """Begin a new arraystruct action for use in a ``with`` context.
129
+
130
+ Args:
131
+ path: relative access layer path within this context
132
+ timebase: path to the timebase for this coordinate (an empty string for
133
+ non-dynamic array of structures)
134
+ size: the size of the array of structures (only relevant when writing data)
135
+
136
+ Returns:
137
+ The created context.
138
+ """
139
+ status, ctx, size = ll_interface.begin_arraystruct_action(
140
+ self.ctx, path, timebase, size
141
+ )
142
+ if status != 0:
143
+ raise LowlevelError("arraystruct_action", status)
144
+ return ALArrayStructContext(ctx, size)
145
+
146
+ def read_data(self, path: str, timebasepath: str, datatype: int, dim: int) -> Any:
147
+ """Call ual_read_data with this context."""
148
+ status, data = ll_interface.read_data(
149
+ self.ctx, path, timebasepath, datatype, dim
150
+ )
151
+ if status != 0:
152
+ raise LowlevelError(f"read data at {path!r}", status)
153
+ return data
154
+
155
+ def delete_data(self, path: str) -> None:
156
+ """Call ual_delete_data with this context."""
157
+ status = ll_interface.delete_data(self.ctx, path)
158
+ if status != 0:
159
+ raise LowlevelError(f"delete data at {path!r}", status)
160
+
161
+ def write_data(self, path: str, timebasepath: str, data: Any) -> None:
162
+ """Call ual_write_data with this context."""
163
+ status = ll_interface.write_data(self.ctx, path, timebasepath, data)
164
+ if status != 0:
165
+ raise LowlevelError(f"write data at {path!r}: {status=}")
166
+
167
+ def list_all_occurrences(self, ids_name: str) -> List[int]:
168
+ """List all occurrences of this IDS."""
169
+ status, occurrences = ll_interface.get_occurrences(self.ctx, ids_name)
170
+ if status != 0:
171
+ raise LowlevelError(f"list occurrences for {ids_name!r}", status)
172
+ if occurrences is not None:
173
+ return list(occurrences)
174
+ return []
175
+
176
+ def close(self):
177
+ """Close this ALContext."""
178
+ ll_interface.end_action(self.ctx)
179
+
180
+
181
+ class ALArrayStructContext(ALContext):
182
+ """Helper class that wraps contexts created through al_begin_arraystruct_action."""
183
+
184
+ # Note: slot for "ctx" is defined in ALContext, only declare /additional/ slots:
185
+ __slots__ = ["size", "curindex"]
186
+
187
+ def __init__(self, ctx, size):
188
+ """Construct a new ALContext object
189
+
190
+ Args:
191
+ ctx: Context identifier returned by the AL
192
+ size: size of the AoS returned by the AL
193
+ """
194
+ self.ctx = ctx
195
+ self.size = size
196
+ """AoS size"""
197
+ self.curindex = 0
198
+ """Current iteration index of this AoS context"""
199
+
200
+ def __enter__(self):
201
+ return self, self.size
202
+
203
+ def iterate_over_arraystruct(self, step: int) -> None:
204
+ """Call al_iterate_over_arraystruct with this context."""
205
+ status = ll_interface.iterate_over_arraystruct(self.ctx, step)
206
+ if status != 0:
207
+ raise LowlevelError("iterate over arraystruct", status)
208
+ self.curindex += step
209
+
210
+ def iterate_to_index(self, index: int) -> None:
211
+ """Call al_iterate_over_arraystruct to iterate to the provided index."""
212
+ step = index - self.curindex
213
+ if step:
214
+ status = ll_interface.iterate_over_arraystruct(self.ctx, step)
215
+ if status != 0:
216
+ raise LowlevelError("iterate over arraystruct", status)
217
+ self.curindex = index
218
+
219
+
220
+ class LazyALContext:
221
+ """Replacement for ALContext that is used during lazy loading.
222
+
223
+ This class implements ``global_action``, ``slice_action`` and ``read_data``, such
224
+ that it can be used as a drop-in replacement in ``imas.db_entry._get_children``
225
+ and only custom logic is needed for IDSStructArray there.
226
+
227
+ This class tracks:
228
+
229
+ - The ALDBEntryImpl object which was used for get() / get_slice().
230
+ - The context object from that ALDBEntryImpl (such that we can detect if the
231
+ underlying AL context was closed or replaced).
232
+ - Potentially a parent LazyALContext for nested contexts (looking at you,
233
+ arraystruct_action!).
234
+ - The ALContext method and arguments that we need to call on the ALContext we obtain
235
+ from our parent, to obtain the actual ALContext we should use for loading data.
236
+ - The NBC map that ``imas.db_entry._get_children`` needs when lazy loading
237
+ children of an IDSStructArray.
238
+
239
+ When constructing a LazyALContext, you need to supply either the ``dbentry`` and
240
+ ``nbc_map``, or a ``parent_ctx``.
241
+ """
242
+
243
+ __slots__ = [
244
+ "dbentry",
245
+ "dbentry_ctx",
246
+ "parent_ctx",
247
+ "method",
248
+ "args",
249
+ "nbc_map",
250
+ "time_mode",
251
+ "context",
252
+ ]
253
+
254
+ def __init__(
255
+ self,
256
+ parent_ctx: Optional["LazyALContext"] = None,
257
+ method: Optional[Callable] = None,
258
+ args: Tuple = (),
259
+ *,
260
+ dbentry: Optional["ALDBEntryImpl"] = None,
261
+ nbc_map: Optional["NBCPathMap"] = None,
262
+ time_mode: Optional[int] = None,
263
+ ) -> None:
264
+ self.dbentry = dbentry or (parent_ctx and parent_ctx.dbentry)
265
+ """ALDBEntryImpl object that created us, or our parent."""
266
+ self.dbentry_ctx = self.dbentry._db_ctx
267
+ """The ALContext of the ALDBEntryImpl at the time of get/get_slice."""
268
+ self.parent_ctx = parent_ctx
269
+ """Optional parent context that provides our parent ALContext."""
270
+ self.method = method
271
+ """Method we need to call with our parent ALContext to get our ALContext."""
272
+ self.args = args
273
+ """Additional arguments we need to supply to self.method"""
274
+ self.nbc_map = nbc_map or (parent_ctx and parent_ctx.nbc_map)
275
+ """NBC map for _get_children() when lazy loading IDSStructArray items."""
276
+ if time_mode is None and parent_ctx:
277
+ time_mode = parent_ctx.time_mode
278
+ self.time_mode = time_mode
279
+ """Time mode used by the IDS being lazy loaded."""
280
+ self.context = None
281
+ """Potential weak reference to opened context."""
282
+
283
+ def get_context(self) -> ALContext:
284
+ """Create and yield the actual ALContext."""
285
+ if self.dbentry._db_ctx is not self.dbentry_ctx:
286
+ raise RuntimeError(
287
+ "Cannot lazy load the requested data: the data entry is no longer "
288
+ "available for reading. Hint: did you close() the DBEntry?"
289
+ )
290
+
291
+ # Try to retrieve context from the cache
292
+ ctx = self.context and self.context() # dereference weakref.ref if it exists
293
+ if ctx:
294
+ # Close all sub-contexts still alive in the cache
295
+ cache = self.dbentry._lazy_ctx_cache
296
+ while cache and cache[-1] is not ctx:
297
+ cache.pop().close()
298
+ if not cache or cache[-1] is not ctx:
299
+ logger.warning(
300
+ "Found an empty AL context cache: This should not happen, please "
301
+ "report this bug to the imas-python developers."
302
+ )
303
+ else:
304
+ return ctx
305
+
306
+ if self.parent_ctx:
307
+ # First convert our parent LazyALContext to an actual ALContext
308
+ parent = self.parent_ctx.get_context()
309
+ # Now we can create our ALContext:
310
+ ctx = self.method(parent, *self.args)
311
+ # Add context to the cache and store a weak reference to it
312
+ self.dbentry._lazy_ctx_cache.append(ctx)
313
+ self.context = weakref.ref(ctx)
314
+ return ctx
315
+ # Note that we do not close the ctx, that happens when it is evicted
316
+ # from the cache
317
+
318
+ else:
319
+ # Purge the cache to close open contexts from other IDSs (IMAS-5603)
320
+ cache = self.dbentry._lazy_ctx_cache
321
+ while cache:
322
+ cache.pop().close()
323
+ return self.dbentry_ctx
324
+
325
+ @contextmanager
326
+ def global_action(self, path: str, rwmode: int) -> Iterator["LazyALContext"]:
327
+ """Lazily start a lowlevel global action, see :meth:`ALContext.global_action`"""
328
+ yield LazyALContext(self, ALContext.global_action, (path, rwmode))
329
+
330
+ @contextmanager
331
+ def slice_action(
332
+ self, path: str, rwmode: int, time_requested: float, interpolation_method: int
333
+ ) -> Iterator["LazyALContext"]:
334
+ """Lazily start a lowlevel slice action, see :meth:`ALContext.slice_action`"""
335
+ yield LazyALContext(
336
+ self,
337
+ ALContext.slice_action,
338
+ (path, rwmode, time_requested, interpolation_method),
339
+ )
340
+
341
+ @contextmanager
342
+ def timerange_action(
343
+ self,
344
+ path: str,
345
+ rwmode: int,
346
+ tmin: float,
347
+ tmax: float,
348
+ dtime: Optional[numpy.ndarray],
349
+ interpolation_method: int,
350
+ ) -> Iterator["LazyALContext"]:
351
+ """Lazily start a lowlevel timerange action, see
352
+ :meth:`ALContext.timerange_action`.
353
+ """
354
+ yield LazyALContext(
355
+ self,
356
+ ALContext.timerange_action,
357
+ (path, rwmode, tmin, tmax, dtime, interpolation_method),
358
+ )
359
+
360
+ def arraystruct_action(
361
+ self, path: str, timebase: str, size: int
362
+ ) -> "LazyALArrayStructContext":
363
+ """Lazily start an arraystruct action."""
364
+ return LazyALArrayStructContext(
365
+ self, ALContext.arraystruct_action, (path, timebase, size)
366
+ )
367
+
368
+
369
+ class LazyALArrayStructContext(LazyALContext):
370
+ """Subclass for lazy array struct contexts."""
371
+
372
+ __slots__ = ()
373
+ # We're sure that this returns an AlArrayStructContext
374
+ get_context: Callable[..., ALArrayStructContext]
375
+
376
+ def iterate_to_index(self, index: int) -> "LazyALArrayStructChildContext":
377
+ """Return a lazy context that can be used by the child structure at index."""
378
+ return LazyALArrayStructChildContext(self, None, (index,))
379
+
380
+
381
+ class LazyALArrayStructChildContext(LazyALContext):
382
+ """Subclass that allows an array of structures child structure to read data."""
383
+
384
+ __slots__ = ()
385
+ parent_ctx: LazyALArrayStructContext
386
+
387
+ def get_context(self) -> ALContext:
388
+ # Override get_context to iterate to the correct index
389
+ context = self.parent_ctx.get_context()
390
+ context.iterate_to_index(self.args[0])
391
+ return context