QuLab 2.0.9__tar.gz → 2.1.1__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 (97) hide show
  1. {qulab-2.0.9 → qulab-2.1.1}/PKG-INFO +1 -1
  2. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/PKG-INFO +1 -1
  3. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/SOURCES.txt +3 -1
  4. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/__init__.py +1 -1
  5. qulab-2.0.9/qulab/scan/query_record.py → qulab-2.1.1/qulab/scan/query.py +8 -4
  6. qulab-2.1.1/qulab/scan/record.py +448 -0
  7. qulab-2.1.1/qulab/scan/recorder.py +241 -0
  8. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/scan.py +219 -215
  9. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/server.py +4 -6
  10. qulab-2.1.1/qulab/scan/space.py +172 -0
  11. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/utils.py +48 -0
  12. qulab-2.1.1/qulab/version.py +1 -0
  13. qulab-2.0.9/qulab/scan/recorder.py +0 -647
  14. qulab-2.0.9/qulab/version.py +0 -1
  15. {qulab-2.0.9 → qulab-2.1.1}/LICENSE +0 -0
  16. {qulab-2.0.9 → qulab-2.1.1}/MANIFEST.in +0 -0
  17. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/dependency_links.txt +0 -0
  18. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/entry_points.txt +0 -0
  19. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/requires.txt +0 -0
  20. {qulab-2.0.9 → qulab-2.1.1}/QuLab.egg-info/top_level.txt +0 -0
  21. {qulab-2.0.9 → qulab-2.1.1}/README.md +0 -0
  22. {qulab-2.0.9 → qulab-2.1.1}/pyproject.toml +0 -0
  23. {qulab-2.0.9 → qulab-2.1.1}/qulab/__init__.py +0 -0
  24. {qulab-2.0.9 → qulab-2.1.1}/qulab/__main__.py +0 -0
  25. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/__init__.py +0 -0
  26. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/__main__.py +0 -0
  27. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/config.py +0 -0
  28. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/dataset.py +0 -0
  29. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/event_queue.py +0 -0
  30. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/mainwindow.py +0 -0
  31. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/monitor.py +0 -0
  32. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/ploter.py +0 -0
  33. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/qt_compat.py +0 -0
  34. {qulab-2.0.9 → qulab-2.1.1}/qulab/monitor/toolbar.py +0 -0
  35. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/curd.py +0 -0
  36. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/expression.py +0 -0
  37. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/models.py +0 -0
  38. {qulab-2.0.9 → qulab-2.1.1}/qulab/scan/optimize.py +0 -0
  39. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/__init__.py +0 -0
  40. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/__main__.py +0 -0
  41. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/backend/__init__.py +0 -0
  42. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/backend/redis.py +0 -0
  43. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/base_dataset.py +0 -0
  44. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/chunk.py +0 -0
  45. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/dataset.py +0 -0
  46. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/file.py +0 -0
  47. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/__init__.py +0 -0
  48. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/base.py +0 -0
  49. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/config.py +0 -0
  50. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/file.py +0 -0
  51. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/ipy.py +0 -0
  52. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/models.py +0 -0
  53. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/record.py +0 -0
  54. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/report.py +0 -0
  55. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/models/tag.py +0 -0
  56. {qulab-2.0.9 → qulab-2.1.1}/qulab/storage/storage.py +0 -0
  57. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/__init__.py +0 -0
  58. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/chat.py +0 -0
  59. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/device/__init__.py +0 -0
  60. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/device/basedevice.py +0 -0
  61. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/device/loader.py +0 -0
  62. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/device/utils.py +0 -0
  63. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/drivers/FakeInstrument.py +0 -0
  64. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/drivers/__init__.py +0 -0
  65. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/ipy_events.py +0 -0
  66. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/__init__.py +0 -0
  67. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/bencoder.py +0 -0
  68. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/cli.py +0 -0
  69. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/dhcp.py +0 -0
  70. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/dhcpd.py +0 -0
  71. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/kad.py +0 -0
  72. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/kcp.py +0 -0
  73. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/net/nginx.py +0 -0
  74. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/progress.py +0 -0
  75. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/__init__.py +0 -0
  76. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/client.py +0 -0
  77. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/exceptions.py +0 -0
  78. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/msgpack.py +0 -0
  79. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/msgpack.pyi +0 -0
  80. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/rpc.py +0 -0
  81. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/serialize.py +0 -0
  82. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/server.py +0 -0
  83. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/socket.py +0 -0
  84. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/utils.py +0 -0
  85. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/worker.py +0 -0
  86. {qulab-2.0.9 → qulab-2.1.1}/qulab/sys/rpc/zmq_socket.py +0 -0
  87. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/__init__.py +0 -0
  88. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/__main__.py +0 -0
  89. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/_autoplot.py +0 -0
  90. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/plot_layout.py +0 -0
  91. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/plot_seq.py +0 -0
  92. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/qdat.py +0 -0
  93. {qulab-2.0.9 → qulab-2.1.1}/qulab/visualization/widgets.py +0 -0
  94. {qulab-2.0.9 → qulab-2.1.1}/setup.cfg +0 -0
  95. {qulab-2.0.9 → qulab-2.1.1}/setup.py +0 -0
  96. {qulab-2.0.9 → qulab-2.1.1}/src/qulab.h +0 -0
  97. {qulab-2.0.9 → qulab-2.1.1}/tests/test_scan.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QuLab
3
- Version: 2.0.9
3
+ Version: 2.1.1
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QuLab
3
- Version: 2.0.9
3
+ Version: 2.1.1
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -27,10 +27,12 @@ qulab/scan/curd.py
27
27
  qulab/scan/expression.py
28
28
  qulab/scan/models.py
29
29
  qulab/scan/optimize.py
30
- qulab/scan/query_record.py
30
+ qulab/scan/query.py
31
+ qulab/scan/record.py
31
32
  qulab/scan/recorder.py
32
33
  qulab/scan/scan.py
33
34
  qulab/scan/server.py
35
+ qulab/scan/space.py
34
36
  qulab/scan/utils.py
35
37
  qulab/storage/__init__.py
36
38
  qulab/storage/__main__.py
@@ -1,3 +1,3 @@
1
1
  from .expression import Expression, Symbol
2
- from .query_record import get_record, lookup, lookup_list
2
+ from .query import get_record, lookup, lookup_list
3
3
  from .scan import Scan
@@ -9,10 +9,11 @@ from IPython.display import display
9
9
 
10
10
  from qulab.sys.rpc.zmq_socket import ZMQContextManager
11
11
 
12
- from .recorder import Record
12
+ from .record import Record
13
+ from .scan import default_server
13
14
 
14
15
 
15
- def get_record(id, database='tcp://127.0.0.1:6789'):
16
+ def get_record(id, database=default_server) -> Record:
16
17
  if isinstance(database, str) and database.startswith('tcp://'):
17
18
  with ZMQContextManager(zmq.DEALER, connect=database) as socket:
18
19
  socket.send_pyobj({
@@ -20,7 +21,10 @@ def get_record(id, database='tcp://127.0.0.1:6789'):
20
21
  'record_id': id
21
22
  })
22
23
  d = dill.loads(socket.recv_pyobj())
23
- return Record(id, database, d)
24
+ d.id = id
25
+ d.database = database
26
+ d._file = None
27
+ return d
24
28
  else:
25
29
  from .models import Record as RecordInDB
26
30
  from .models import create_engine, sessionmaker
@@ -292,7 +296,7 @@ def _on_tags_submit(tags, ui_widgets):
292
296
  _update_view(ui_widgets)
293
297
 
294
298
 
295
- def lookup(app=None, limit=10, database='tcp://127.0.0.1:6789'):
299
+ def lookup(app=None, limit=10, database=default_server):
296
300
  after = widgets.DatePicker()
297
301
  before = widgets.DatePicker()
298
302
  app_prefix = widgets.Label('App:')
@@ -0,0 +1,448 @@
1
+ import itertools
2
+ import sys
3
+ import uuid
4
+ from pathlib import Path
5
+ from threading import Lock
6
+ from types import EllipsisType
7
+
8
+ import dill
9
+ import numpy as np
10
+ import zmq
11
+
12
+ from qulab.sys.rpc.zmq_socket import ZMQContextManager
13
+
14
+ from .space import OptimizeSpace, Space
15
+
16
+ _notgiven = object()
17
+
18
+
19
+ def random_path(base):
20
+ while True:
21
+ s = uuid.uuid4().hex
22
+ path = base / s[:2] / s[2:4] / s[4:6] / s[6:]
23
+ if not path.exists():
24
+ return path
25
+
26
+
27
+ def index_in_slice(slice_obj: slice | int, index: int):
28
+ if isinstance(slice_obj, int):
29
+ return slice_obj == index
30
+ start, stop, step = slice_obj.start, slice_obj.stop, slice_obj.step
31
+ if start is None:
32
+ start = 0
33
+ if step is None:
34
+ step = 1
35
+ if stop is None:
36
+ stop = sys.maxsize
37
+
38
+ if step > 0:
39
+ return start <= index < stop and (index - start) % step == 0
40
+ else:
41
+ return stop < index <= start and (index - start) % step == 0
42
+
43
+
44
+ class BufferList():
45
+
46
+ def __init__(self, file=None, slice=None):
47
+ self._list = []
48
+ self.lu = ()
49
+ self.rd = ()
50
+ self.inner_shape = ()
51
+ self.file = file
52
+ self._slice = slice
53
+ self._lock = Lock()
54
+ self._data_id = None
55
+
56
+ def __repr__(self):
57
+ return f"<BufferList: shape={self.shape}, lu={self.lu}, rd={self.rd}, slice={self._slice}>"
58
+
59
+ def __getstate__(self):
60
+ self.flush()
61
+ if isinstance(self.file, Path):
62
+ file = '/'.join(self.file.parts[-4:])
63
+ else:
64
+ file = self.file
65
+ return {
66
+ 'file': file,
67
+ 'lu': self.lu,
68
+ 'rd': self.rd,
69
+ 'inner_shape': self.inner_shape,
70
+ }
71
+
72
+ def __setstate__(self, state):
73
+ self.file = state['file']
74
+ self.lu = state['lu']
75
+ self.rd = state['rd']
76
+ self.inner_shape = state['inner_shape']
77
+ self._list = []
78
+ self._slice = None
79
+ self._lock = Lock()
80
+ self._data_id = None
81
+
82
+ @property
83
+ def shape(self):
84
+ return tuple([i - j
85
+ for i, j in zip(self.rd, self.lu)]) + self.inner_shape
86
+
87
+ def flush(self):
88
+ if not self._list:
89
+ return
90
+ if isinstance(self.file, Path):
91
+ with self._lock:
92
+ with open(self.file, 'ab') as f:
93
+ for item in self._list:
94
+ dill.dump(item, f)
95
+ self._list.clear()
96
+
97
+ def append(self, pos, value, dims=None):
98
+ if dims is not None:
99
+ if any([p != 0 for i, p in enumerate(pos) if i not in dims]):
100
+ return
101
+ pos = tuple([pos[i] for i in dims])
102
+ self.lu = tuple([min(i, j) for i, j in zip(pos, self.lu)])
103
+ self.rd = tuple([max(i + 1, j) for i, j in zip(pos, self.rd)])
104
+ if hasattr(value, 'shape'):
105
+ if self.inner_shape is None:
106
+ self.inner_shape = value.shape
107
+ elif self.inner_shape != value.shape:
108
+ self.inner_shape = ()
109
+ self._list.append((pos, value))
110
+ if len(self._list) > 1000:
111
+ self.flush()
112
+
113
+ def _iter_file(self):
114
+ if isinstance(self.file, Path) and self.file.exists():
115
+ with self._lock:
116
+ with open(self.file, 'rb') as f:
117
+ while True:
118
+ try:
119
+ pos, value = dill.load(f)
120
+ yield pos, value
121
+ except EOFError:
122
+ break
123
+
124
+ def iter(self):
125
+ if self._data_id is None:
126
+ for pos, value in itertools.chain(self._iter_file(), self._list):
127
+ if not self._slice:
128
+ yield pos, value
129
+ elif all(
130
+ [index_in_slice(s, i) for s, i in zip(self._slice, pos)]):
131
+ if self.inner_shape:
132
+ yield pos, value[self._slice[len(pos):]]
133
+ else:
134
+ yield pos, value
135
+ else:
136
+ server, record_id, key = self._data_id
137
+ with ZMQContextManager(zmq.DEALER, connect=server) as socket:
138
+ socket.send_pyobj({
139
+ 'method': 'bufferlist_slice',
140
+ 'record_id': record_id,
141
+ 'key': key,
142
+ 'slice': self._slice
143
+ })
144
+ ret = socket.recv_pyobj()
145
+ yield from ret
146
+
147
+ def value(self):
148
+ d = []
149
+ for pos, value in self.iter():
150
+ d.append(value)
151
+ return d
152
+
153
+ def pos(self):
154
+ p = []
155
+ for pos, value in self.iter():
156
+ p.append(pos)
157
+ return p
158
+
159
+ def items(self):
160
+ p, d = [], []
161
+ for pos, value in self.iter():
162
+ p.append(pos)
163
+ d.append(value)
164
+ return p, d
165
+
166
+ def array(self):
167
+ pos, data = self.items()
168
+ if self._slice:
169
+ pos = np.asarray(pos)
170
+ lu = tuple(np.min(pos, axis=0))
171
+ rd = tuple(np.max(pos, axis=0) + 1)
172
+ pos = np.asarray(pos) - np.asarray(lu)
173
+ shape = []
174
+ for k, (s, i, j) in enumerate(zip(self._slice, rd, lu)):
175
+ if s.step is not None:
176
+ pos[:, k] = pos[:, k] / s.step
177
+ shape.append(round(np.ceil((i - j) / s.step)))
178
+ else:
179
+ shape.append(i - j)
180
+ shape = tuple(shape)
181
+ else:
182
+ shape = tuple([i - j for i, j in zip(self.rd, self.lu)])
183
+ pos = np.asarray(pos) - np.asarray(self.lu)
184
+ data = np.asarray(data)
185
+ inner_shape = data.shape[1:]
186
+ x = np.full(shape + inner_shape, np.nan, dtype=data[0].dtype)
187
+ x.__setitem__(tuple(pos.T), data)
188
+ return x
189
+
190
+ def _full_slice(self, slice_tuple: slice
191
+ | tuple[slice | int | EllipsisType, ...]):
192
+ ndim = len(self.lu)
193
+ if self.inner_shape:
194
+ ndim += len(self.inner_shape)
195
+
196
+ if isinstance(slice_tuple, slice):
197
+ slice_tuple = (
198
+ slice_tuple, ) + (slice(0, sys.maxsize, 1), ) * (ndim - 1)
199
+ if slice_tuple is Ellipsis:
200
+ slice_tuple = (slice(0, sys.maxsize, 1), ) * ndim
201
+ else:
202
+ head, tail = (), ()
203
+ for i, s in enumerate(slice_tuple):
204
+ if s is Ellipsis:
205
+ head = slice_tuple[:i]
206
+ tail = slice_tuple[i + 1:]
207
+ break
208
+ else:
209
+ head = slice_tuple
210
+ tail = ()
211
+ slice_tuple = head + (slice(
212
+ 0, sys.maxsize, 1), ) * (ndim - len(head) - len(tail)) + tail
213
+ slice_list = []
214
+ contract = []
215
+ reversed = []
216
+ for i, s in enumerate(slice_tuple):
217
+ if isinstance(s, int):
218
+ if s >= 0:
219
+ slice_list.append(slice(s, s + 1, 1))
220
+ elif i < len(self.lu):
221
+ s = self.rd[i] + s
222
+ slice_list.append(slice(s, s + 1, 1))
223
+ else:
224
+ slice_list.append(slice(s, s - 1, -1))
225
+ contract.append(i)
226
+ else:
227
+ start, stop, step = s.start, s.stop, s.step
228
+ if step is None:
229
+ step = 1
230
+ if step < 0 and i < len(self.lu):
231
+ step = -step
232
+ reversed.append(i)
233
+ if start is None and stop is None:
234
+ start, stop = 0, sys.maxsize
235
+ elif start is None:
236
+ start, stop = self.lu[i], sys.maxsize
237
+ elif stop is None:
238
+ start, stop = 0, start + self.lu[i]
239
+ else:
240
+ start, stop = stop + self.lu[i] + 1, start + self.lu[
241
+ i] + 1
242
+
243
+ if start is None:
244
+ start = 0
245
+ elif start < 0 and i < len(self.lu):
246
+ start = self.rd[i] + start
247
+ if step is None:
248
+ step = 1
249
+ if stop is None:
250
+ stop = sys.maxsize
251
+ elif stop < 0 and i < len(self.lu):
252
+ stop = self.rd[i] + stop
253
+
254
+ slice_list.append(slice(start, stop, step))
255
+ return tuple(slice_list), contract, reversed
256
+
257
+ def __getitem__(self, slice_tuple: slice | EllipsisType
258
+ | tuple[slice | int | EllipsisType, ...]):
259
+ self._slice, contract, reversed = self._full_slice(slice_tuple)
260
+ ret = self.array()
261
+ slices = []
262
+ for i, s in enumerate(self._slice):
263
+ if i in contract:
264
+ slices.append(0)
265
+ elif isinstance(s, slice):
266
+ if i in reversed:
267
+ slices.append(slice(None, None, -1))
268
+ else:
269
+ slices.append(slice(None, None, 1))
270
+ ret = ret.__getitem__(tuple(slices))
271
+ self._slice = None
272
+ return ret
273
+
274
+
275
+ class Record():
276
+
277
+ def __init__(self, id, database, description=None):
278
+ self.id = id
279
+ self.database = database
280
+ self.description = description
281
+ self._items = {}
282
+ self._pos = []
283
+ self._last_vars = set()
284
+ self._file = None
285
+
286
+ if self.is_local_record():
287
+ self.database = Path(self.database)
288
+ self._file = random_path(self.database / 'objects')
289
+ self._file.parent.mkdir(parents=True, exist_ok=True)
290
+
291
+ def __getstate__(self) -> dict:
292
+ return {
293
+ 'id': self.id,
294
+ 'description': self.description,
295
+ '_items': self._items,
296
+ }
297
+
298
+ def __setstate__(self, state: dict):
299
+ self.id = state['id']
300
+ self.description = state['description']
301
+ self._items = state['_items']
302
+ self._pos = []
303
+ self._last_vars = set()
304
+ self.database = None
305
+ self._file = None
306
+
307
+ @property
308
+ def axis(self):
309
+ return self.description.get('axis', {})
310
+
311
+ def is_local_record(self):
312
+ return not self.is_cache_record() and not self.is_remote_record()
313
+
314
+ def is_cache_record(self):
315
+ return self.database is None
316
+
317
+ def is_remote_record(self):
318
+ return isinstance(self.database,
319
+ str) and self.database.startswith("tcp://")
320
+
321
+ def __del__(self):
322
+ self.flush()
323
+
324
+ def __getitem__(self, key):
325
+ ret = self.get(key, buffer_to_array=True)
326
+ if isinstance(ret, Space):
327
+ ret = ret.toarray()
328
+ return ret
329
+
330
+ def get(self, key, default=_notgiven, buffer_to_array=False, slice=None):
331
+ if self.is_remote_record():
332
+ with ZMQContextManager(zmq.DEALER,
333
+ connect=self.database) as socket:
334
+ socket.send_pyobj({
335
+ 'method': 'record_getitem',
336
+ 'record_id': self.id,
337
+ 'key': key
338
+ })
339
+ ret = socket.recv_pyobj()
340
+ if isinstance(ret, BufferList):
341
+ if buffer_to_array:
342
+ socket.send_pyobj({
343
+ 'method': 'bufferlist_slice',
344
+ 'record_id': self.id,
345
+ 'key': key,
346
+ 'slice': slice
347
+ })
348
+ lst = socket.recv_pyobj()
349
+ ret._list = lst
350
+ ret._slice = slice
351
+ return ret.array()
352
+ else:
353
+ ret._data_id = self.database, self.id, key
354
+ return ret
355
+ else:
356
+ return ret
357
+ else:
358
+ if default is _notgiven:
359
+ d = self._items.get(key)
360
+ else:
361
+ d = self._items.get(key, default)
362
+ if isinstance(d, BufferList):
363
+ if isinstance(d.file, str):
364
+ d.file = self._file.parent.parent.parent.parent / d.file
365
+ d._slice = slice
366
+ if buffer_to_array:
367
+ return d.array()
368
+ else:
369
+ return d
370
+ else:
371
+ return d
372
+
373
+ def keys(self):
374
+ if self.is_remote_record():
375
+ with ZMQContextManager(zmq.DEALER,
376
+ connect=self.database) as socket:
377
+ socket.send_pyobj({
378
+ 'method': 'record_keys',
379
+ 'record_id': self.id
380
+ })
381
+ return socket.recv_pyobj()
382
+ else:
383
+ return list(self._items.keys())
384
+
385
+ def append(self, level, step, position, variables):
386
+ if level < 0:
387
+ self.flush()
388
+ return
389
+
390
+ for key in set(variables.keys()) - self._last_vars:
391
+ if key not in self.axis:
392
+ self.axis[key] = tuple(range(level + 1))
393
+
394
+ self._last_vars = set(variables.keys())
395
+
396
+ if level >= len(self._pos):
397
+ l = level + 1 - len(self._pos)
398
+ self._pos.extend(([0] * (l - 1)) + [position])
399
+ pos = tuple(self._pos)
400
+ elif level == len(self._pos) - 1:
401
+ self._pos[-1] = position
402
+ pos = tuple(self._pos)
403
+ else:
404
+ self._pos = self._pos[:level + 1]
405
+ self._pos[-1] = position
406
+ pos = tuple(self._pos)
407
+ self._pos[-1] += 1
408
+
409
+ for key, value in variables.items():
410
+ if self.axis[key] == ():
411
+ if key not in self._items:
412
+ self._items[key] = value
413
+ elif level == self.axis[key][-1]:
414
+ if key not in self._items:
415
+ if self.is_local_record():
416
+ bufferlist_file = random_path(self.database /
417
+ 'objects')
418
+ bufferlist_file.parent.mkdir(parents=True,
419
+ exist_ok=True)
420
+ self._items[key] = BufferList(bufferlist_file)
421
+ else:
422
+ self._items[key] = BufferList()
423
+ self._items[key].lu = pos
424
+ self._items[key].rd = tuple([i + 1 for i in pos])
425
+ self._items[key].append(pos, value, self.axis[key])
426
+ elif isinstance(self._items[key], BufferList):
427
+ self._items[key].append(pos, value, self.axis[key])
428
+
429
+ def flush(self):
430
+ if self.is_remote_record() or self.is_cache_record():
431
+ return
432
+
433
+ for key, value in self._items.items():
434
+ if isinstance(value, BufferList):
435
+ value.flush()
436
+
437
+ with open(self._file, 'wb') as f:
438
+ dill.dump(self, f)
439
+
440
+ def __repr__(self):
441
+ return f"<Record: id={self.id} app={self.description['app']}, keys={self.keys()}>"
442
+
443
+ # def _repr_html_(self):
444
+ # return f"""
445
+ # <h3>Record: id={self.id}, app={self.description['app']}</h3>
446
+ # <p>keys={self.keys()}</p>
447
+ # <p>axis={self.axis}</p>
448
+ # """