QuLab 2.2.4__cp311-cp311-macosx_10_9_universal2.whl → 2.2.6__cp311-cp311-macosx_10_9_universal2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QuLab
3
- Version: 2.2.4
3
+ Version: 2.2.6
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,7 +1,7 @@
1
1
  qulab/__init__.py,sha256=P-Mx2p4TVmL91SoxoeXcj8Qm0x4xUf5Q_FLk0Yc_gIQ,138
2
2
  qulab/__main__.py,sha256=ZC1NKaoxKyy60DaCfB8vYnB1z3RXQ2j8E1sRZ4A8sXE,428
3
- qulab/fun.cpython-311-darwin.so,sha256=Qsir5YKvUe8Be3Xpd6-KqhvHgjZlHYWVphWeOMODxaY,159616
4
- qulab/version.py,sha256=q2oNXq5isDMLWwNWH0ZSaEk8JVgaJMDOwWNURaUZwW0,21
3
+ qulab/fun.cpython-311-darwin.so,sha256=VU-AxnPqaotR3zCtI9i09cvMWyTLX6x32LzM9YIkeZg,159616
4
+ qulab/version.py,sha256=CsrswO-ECCDHSY48Nv5lOOdRgL8EGAixBOHDf7PkRZA,21
5
5
  qulab/monitor/__init__.py,sha256=nTHelnDpxRS_fl_B38TsN0njgq8eVTEz9IAnN3NbDlM,42
6
6
  qulab/monitor/__main__.py,sha256=w3yUcqq195LzSnXTkQcuC1RSFRhy4oQ_PEBmucXguME,97
7
7
  qulab/monitor/config.py,sha256=fQ5JcsMApKc1UwANEnIvbDQZl8uYW0tle92SaYtX9lI,744
@@ -17,11 +17,11 @@ qulab/scan/curd.py,sha256=thq_qfi3qng3Zx-1uhNG64IQhGCuum_LR4MOKnS8cDI,6896
17
17
  qulab/scan/expression.py,sha256=-aTYbjFQNI1mwOcoSBztqhKfGJpu_n4a1QnWro_xnTU,15694
18
18
  qulab/scan/models.py,sha256=5Jpo25WGMWs0GtLzYLsWO61G3-FFYx5BHhBr2b6rOTE,17681
19
19
  qulab/scan/optimize.py,sha256=vErjRTCtn2MwMF5Xyhs1P4gHF2IFHv_EqxsUvH_4y7k,2287
20
- qulab/scan/query.py,sha256=ppCChKzaTbSLcaNxdgx4CIAsw5rqaSC3flbd2y0kkaU,11541
21
- qulab/scan/record.py,sha256=LDbWPcBzWAmnEAqDv7Oxrwf7a_A29rk_jd9zRqQYOgc,19586
22
- qulab/scan/scan.py,sha256=dRtXewI8W9Fe5ZQsMz56XU-dopfa-MleEihYS0os7Eg,34422
23
- qulab/scan/server.py,sha256=EszeCBAHyZDdy-fq3RHPu696Do6qzsXmdamK6nkEIsw,12215
24
- qulab/scan/space.py,sha256=XEWQnlRYPix65LyiT6KLOK7WG2a11FJJ_M4H_dZ6Z-E,5209
20
+ qulab/scan/query.py,sha256=-5uHMhXSyGovK1oy_uUbGVEbRFzaMBkP78ZMNfI3jD0,11809
21
+ qulab/scan/record.py,sha256=xgmhtL9l019HYHOHvyzK1vxqagGam51rAM7sj9ZWm5U,21103
22
+ qulab/scan/scan.py,sha256=YPGeuBwxepwmKvz3HE0X5euHqPQGSc9NUGEPEiPRiso,34884
23
+ qulab/scan/server.py,sha256=u6in_8-AfcCA4pGAolNR_2emlewGdc3bN6c0pDygBWY,14158
24
+ qulab/scan/space.py,sha256=jDmFY2FcYe-Qcp16YBhAEdTKibOAsHQUnpDrpcPhvzg,5279
25
25
  qulab/scan/utils.py,sha256=Pg_tCf3SUKTiPSBqb6Enkgx4bAyQJAkDGe9uYys1xVU,3613
26
26
  qulab/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  qulab/storage/__main__.py,sha256=3emxxRry8BB0m8hUZvJ_oBqkPy7ksV7flHB_KEDXZuI,1692
@@ -70,7 +70,7 @@ qulab/sys/rpc/server.py,sha256=e3R0gwOHpLEkSp7Tb43FMSDvqSG-pjrkskdISKQRseE,713
70
70
  qulab/sys/rpc/socket.py,sha256=e3R0gwOHpLEkSp7Tb43FMSDvqSG-pjrkskdISKQRseE,713
71
71
  qulab/sys/rpc/utils.py,sha256=6YGFOkY7o09lkA_I1FIP9_1Up3k2F1KOkftvu0_8lxo,594
72
72
  qulab/sys/rpc/worker.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- qulab/sys/rpc/zmq_socket.py,sha256=dx7ROCsLGQ50rGu5b0CSRL2P2aO5fbxOG9pS0T-XXCo,8147
73
+ qulab/sys/rpc/zmq_socket.py,sha256=2uc9MjCRUwWm6iPmi2VGnOYNf9yWFSUzEVYrx0jZvPU,8272
74
74
  qulab/visualization/__init__.py,sha256=26cuHt3QIJXUb3VaMxlJx3IQTOUVJFKlYBZr7WMP53M,6129
75
75
  qulab/visualization/__main__.py,sha256=9zKK3yZFy0leU40ou6BpRC1Fsetfc1gjjFzIZYIwP6Y,1639
76
76
  qulab/visualization/_autoplot.py,sha256=jddg40dX48Wd8G6NLFA_Kf7z1QxdrZBDS99Xx2GLMqs,14099
@@ -78,9 +78,9 @@ qulab/visualization/plot_layout.py,sha256=clNw9QjE_kVNpIIx2Ob4YhAz2fucPGMuzkoIrO
78
78
  qulab/visualization/plot_seq.py,sha256=lphYF4VhkEdc_wWr1kFBwrx2yujkyFPFaJ3pjr61awI,2693
79
79
  qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
80
80
  qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
81
- QuLab-2.2.4.dist-info/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
82
- QuLab-2.2.4.dist-info/METADATA,sha256=BI6WD3-Wbq6h8F1rHNYnT_0cYgd7y_f0m-L3FddbweM,3510
83
- QuLab-2.2.4.dist-info/WHEEL,sha256=eupBwbXGAhwNAPJSvj5BiShZwdZO8jnQ5yHfv-9aUGw,115
84
- QuLab-2.2.4.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
85
- QuLab-2.2.4.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
86
- QuLab-2.2.4.dist-info/RECORD,,
81
+ QuLab-2.2.6.dist-info/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
82
+ QuLab-2.2.6.dist-info/METADATA,sha256=4vOBx495seNQlHygPaIrLKr2y7C5NtMIhPzwUnnI2r8,3510
83
+ QuLab-2.2.6.dist-info/WHEEL,sha256=eupBwbXGAhwNAPJSvj5BiShZwdZO8jnQ5yHfv-9aUGw,115
84
+ QuLab-2.2.6.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
85
+ QuLab-2.2.6.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
86
+ QuLab-2.2.6.dist-info/RECORD,,
Binary file
qulab/scan/query.py CHANGED
@@ -16,9 +16,13 @@ from .scan import default_server
16
16
  from .server import get_local_record
17
17
 
18
18
 
19
- def get_record(id, database=default_server) -> Record:
19
+ def get_record(id,
20
+ database=default_server,
21
+ socket=None,
22
+ session=None) -> Record:
20
23
  if isinstance(database, str) and database.startswith('tcp://'):
21
- with ZMQContextManager(zmq.DEALER, connect=database) as socket:
24
+ with ZMQContextManager(zmq.DEALER, connect=database,
25
+ socket=socket) as socket:
22
26
  socket.send_pyobj({
23
27
  'method': 'record_description',
24
28
  'record_id': id
@@ -29,12 +33,16 @@ def get_record(id, database=default_server) -> Record:
29
33
  d.id = id
30
34
  d.database = database
31
35
  d._file = None
36
+ d._sock = socket
32
37
  return d
33
38
  else:
34
- db_file = Path(database) / 'data.db'
35
- engine = create_engine(f'sqlite:///{db_file}')
36
- Session = sessionmaker(bind=engine)
37
- with Session() as session:
39
+ if session is None:
40
+ db_file = Path(database) / 'data.db'
41
+ engine = create_engine(f'sqlite:///{db_file}')
42
+ Session = sessionmaker(bind=engine)
43
+ with Session() as session:
44
+ return get_local_record(session, id, database)
45
+ else:
38
46
  return get_local_record(session, id, database)
39
47
 
40
48
 
qulab/scan/record.py CHANGED
@@ -56,6 +56,7 @@ class BufferList():
56
56
  self._slice = slice
57
57
  self._lock = Lock()
58
58
  self._data_id = None
59
+ self._sock = None
59
60
 
60
61
  def __repr__(self):
61
62
  return f"<BufferList: shape={self.shape}, lu={self.lu}, rd={self.rd}, slice={self._slice}>"
@@ -82,6 +83,7 @@ class BufferList():
82
83
  self._slice = None
83
84
  self._lock = Lock()
84
85
  self._data_id = None
86
+ self._sock = None
85
87
 
86
88
  @property
87
89
  def shape(self):
@@ -115,7 +117,8 @@ class BufferList():
115
117
  self.inner_shape = value.shape
116
118
  elif self.inner_shape != value.shape:
117
119
  self.inner_shape = ()
118
- self._list.append((pos, value))
120
+ with self._lock:
121
+ self._list.append((pos, value))
119
122
  if len(self._list) > 1000:
120
123
  self.flush()
121
124
 
@@ -155,15 +158,32 @@ class BufferList():
155
158
  yield pos, value
156
159
  else:
157
160
  server, record_id, key = self._data_id
158
- with ZMQContextManager(zmq.DEALER, connect=server) as socket:
159
- socket.send_pyobj({
160
- 'method': 'bufferlist_slice',
161
- 'record_id': record_id,
162
- 'key': key,
163
- 'slice': self._slice
164
- })
165
- ret = socket.recv_pyobj()
166
- yield from ret
161
+ with ZMQContextManager(zmq.DEALER,
162
+ connect=server,
163
+ socket=self._sock) as socket:
164
+ iter_id = b''
165
+ start = 0
166
+ try:
167
+ while True:
168
+ socket.send_pyobj({
169
+ 'method': 'bufferlist_iter',
170
+ 'record_id': record_id,
171
+ 'iter_id': iter_id,
172
+ 'key': key,
173
+ 'slice': self._slice,
174
+ 'start': start
175
+ })
176
+ iter_id, lst, finished = socket.recv_pyobj()
177
+ start += len(lst)
178
+ yield from lst
179
+ if finished:
180
+ break
181
+ finally:
182
+ if iter_id:
183
+ socket.send_pyobj({
184
+ 'method': 'bufferlist_iter_exit',
185
+ 'iter_id': iter_id
186
+ })
167
187
 
168
188
  def value(self):
169
189
  d = []
@@ -184,7 +204,7 @@ class BufferList():
184
204
  d.append(value)
185
205
  return p, d
186
206
 
187
- def array(self):
207
+ def toarray(self):
188
208
  pos, data = self.items()
189
209
  if self._slice:
190
210
  pos = np.asarray(pos)
@@ -278,7 +298,7 @@ class BufferList():
278
298
  def __getitem__(self, slice_tuple: slice | EllipsisType
279
299
  | tuple[slice | int | EllipsisType, ...]):
280
300
  self._slice, contract, reversed = self._full_slice(slice_tuple)
281
- ret = self.array()
301
+ ret = self.toarray()
282
302
  slices = []
283
303
  for i, s in enumerate(self._slice):
284
304
  if i in contract:
@@ -303,6 +323,15 @@ class Record():
303
323
  self._pos = []
304
324
  self._last_vars = set()
305
325
  self._file = None
326
+ self._sock = None
327
+
328
+ for name, value in self.description['intrinsic_loops'].items():
329
+ self._items[name] = value
330
+
331
+ for level, range_list in description['loops'].items():
332
+ for name, iterable in range_list:
333
+ if name in self.description['independent_variables']:
334
+ self._items[name] = iterable
306
335
 
307
336
  if self.is_local_record():
308
337
  self.database = Path(self.database)
@@ -324,6 +353,7 @@ class Record():
324
353
  self._last_vars = set()
325
354
  self.database = None
326
355
  self._file = None
356
+ self._sock = None
327
357
 
328
358
  @property
329
359
  def axis(self):
@@ -337,7 +367,8 @@ class Record():
337
367
  return cls(config_id)
338
368
  if self.is_remote_record():
339
369
  with ZMQContextManager(zmq.DEALER,
340
- connect=self.database) as socket:
370
+ connect=self.database,
371
+ socket=self._sock) as socket:
341
372
  socket.send_pyobj({
342
373
  'method': 'config_get',
343
374
  'config_id': config_id
@@ -351,6 +382,31 @@ class Record():
351
382
  session.commit()
352
383
  return cls(config)
353
384
 
385
+ def scripts(self, session=None):
386
+ scripts = self.description['entry']['scripts']
387
+ if isinstance(scripts, list):
388
+ return scripts
389
+ else:
390
+ cell_id = scripts
391
+
392
+ if self.is_remote_record():
393
+ with ZMQContextManager(zmq.DEALER,
394
+ connect=self.database,
395
+ socket=self._sock) as socket:
396
+ socket.send_pyobj({
397
+ 'method': 'notebook_history',
398
+ 'cell_id': cell_id
399
+ })
400
+ return socket.recv_pyobj()
401
+ elif self.is_local_record():
402
+ from .models import Cell
403
+ assert session is not None, "session is required for local record"
404
+ cell = session.get(Cell, cell_id)
405
+ return [
406
+ cell.input.text
407
+ for cell in cell.notebook.cells[1:cell.index + 2]
408
+ ]
409
+
354
410
  def is_local_record(self):
355
411
  return not self.is_cache_record() and not self.is_remote_record()
356
412
 
@@ -365,15 +421,13 @@ class Record():
365
421
  self.flush()
366
422
 
367
423
  def __getitem__(self, key):
368
- ret = self.get(key, buffer_to_array=True)
369
- if isinstance(ret, Space):
370
- ret = ret.toarray()
371
- return ret
424
+ return self.get(key, buffer_to_array=True)
372
425
 
373
- def get(self, key, default=_not_given, buffer_to_array=False, slice=None):
426
+ def get(self, key, default=_not_given, buffer_to_array=False):
374
427
  if self.is_remote_record():
375
428
  with ZMQContextManager(zmq.DEALER,
376
- connect=self.database) as socket:
429
+ connect=self.database,
430
+ socket=self._sock) as socket:
377
431
  socket.send_pyobj({
378
432
  'method': 'record_getitem',
379
433
  'record_id': self.id,
@@ -381,20 +435,14 @@ class Record():
381
435
  })
382
436
  ret = socket.recv_pyobj()
383
437
  if isinstance(ret, BufferList):
438
+ ret._data_id = self.database, self.id, key
439
+ ret._sock = socket
384
440
  if buffer_to_array:
385
- socket.send_pyobj({
386
- 'method': 'bufferlist_slice',
387
- 'record_id': self.id,
388
- 'key': key,
389
- 'slice': slice
390
- })
391
- lst = socket.recv_pyobj()
392
- ret._list = lst
393
- ret._slice = slice
394
- return ret.array()
441
+ return ret.toarray()
395
442
  else:
396
- ret._data_id = self.database, self.id, key
397
443
  return ret
444
+ elif isinstance(ret, Space) and buffer_to_array:
445
+ return ret.toarray()
398
446
  else:
399
447
  return ret
400
448
  else:
@@ -407,7 +455,12 @@ class Record():
407
455
  d.file = self._file.parent.parent.parent.parent / d.file
408
456
  d._slice = slice
409
457
  if buffer_to_array:
410
- return d.array()
458
+ return d.toarray()
459
+ else:
460
+ return d
461
+ elif isinstance(d, Space):
462
+ if buffer_to_array:
463
+ return d.toarray()
411
464
  else:
412
465
  return d
413
466
  else:
@@ -416,7 +469,8 @@ class Record():
416
469
  def keys(self):
417
470
  if self.is_remote_record():
418
471
  with ZMQContextManager(zmq.DEALER,
419
- connect=self.database) as socket:
472
+ connect=self.database,
473
+ socket=self._sock) as socket:
420
474
  socket.send_pyobj({
421
475
  'method': 'record_keys',
422
476
  'record_id': self.id
@@ -483,7 +537,8 @@ class Record():
483
537
  def delete(self):
484
538
  if self.is_remote_record():
485
539
  with ZMQContextManager(zmq.DEALER,
486
- connect=self.database) as socket:
540
+ connect=self.database,
541
+ socket=self._sock) as socket:
487
542
  socket.send_pyobj({
488
543
  'method': 'record_delete',
489
544
  'record_id': self.id
@@ -516,36 +571,17 @@ class Record():
516
571
  with z.open('record.pkl', 'w') as f:
517
572
  self.description['entry']['scripts'] = self.scripts()
518
573
  dill.dump((self.description, items), f)
519
-
520
- def scripts(self, session=None):
521
- scripts = self.description['entry']['scripts']
522
- if isinstance(scripts, list):
523
- return scripts
524
- else:
525
- cell_id = scripts
526
-
527
- if self.is_remote_record():
528
- with ZMQContextManager(zmq.DEALER,
529
- connect=self.database) as socket:
530
- socket.send_pyobj({
531
- 'method': 'notebook_history',
532
- 'cell_id': cell_id
533
- })
534
- return socket.recv_pyobj()
535
- elif self.is_local_record():
536
- from .models import Cell
537
- assert session is not None, "session is required for local record"
538
- cell = session.get(Cell, cell_id)
539
- return [
540
- cell.input.text
541
- for cell in cell.notebook.cells[1:cell.index + 2]
542
- ]
574
+ with z.open('config.pkl', 'w') as f:
575
+ f.write(dill.dumps(self.config()))
543
576
 
544
577
  @classmethod
545
578
  def load(cls, file: str):
546
579
  with zipfile.ZipFile(file, 'r') as z:
547
580
  with z.open('record.pkl', 'r') as f:
548
581
  description, items = dill.load(f)
582
+ with z.open('config.pkl', 'r') as f:
583
+ config = dill.load(f)
584
+ description['config'] = config
549
585
  record = cls(None, None, description)
550
586
  for key, value in items.items():
551
587
  if isinstance(value, BufferList):
qulab/scan/scan.py CHANGED
@@ -225,6 +225,7 @@ class Scan():
225
225
  dump_globals: bool = False,
226
226
  max_workers: int = 4,
227
227
  max_promise: int = 100,
228
+ max_message: int = 1000,
228
229
  mixin=None):
229
230
  self.id = task_uuid()
230
231
  self.record = None
@@ -265,11 +266,12 @@ class Scan():
265
266
  self._sem = asyncio.Semaphore(max_promise + 1)
266
267
  self._bar: dict[int, tqdm] = {}
267
268
  self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
268
- self._msg_queue = asyncio.Queue()
269
+ self._msg_queue = asyncio.Queue(max_message)
269
270
  self._prm_queue = asyncio.Queue()
270
271
  self._single_step = True
271
272
  self._max_workers = max_workers
272
273
  self._max_promise = max_promise
274
+ self._max_message = max_message
273
275
  self._executors = ProcessPoolExecutor(max_workers=max_workers)
274
276
 
275
277
  def __del__(self):
@@ -297,7 +299,7 @@ class Scan():
297
299
  self._main_task = None
298
300
  self._bar = {}
299
301
  self._prm_queue = asyncio.Queue()
300
- self._msg_queue = asyncio.Queue()
302
+ self._msg_queue = asyncio.Queue(self._max_message)
301
303
  self._sem = asyncio.Semaphore(self._max_promise + 1)
302
304
  self._executors = ProcessPoolExecutor(max_workers=self._max_workers)
303
305
  for opt in self.description['optimizers'].values():
@@ -344,8 +346,9 @@ class Scan():
344
346
  for k, v in variables.items() if not self.hiden(k)
345
347
  })
346
348
 
347
- def emit(self, current_level, step, position, variables: dict[str, Any]):
348
- self._msg_queue.put_nowait(
349
+ async def emit(self, current_level, step, position, variables: dict[str,
350
+ Any]):
351
+ await self._msg_queue.put(
349
352
  self._emit(current_level, step, position, variables.copy()))
350
353
 
351
354
  def hide(self, name: str):
@@ -552,16 +555,16 @@ class Scan():
552
555
  if isinstance(
553
556
  self.description['database'],
554
557
  str) and self.description['database'].startswith("tcp://"):
555
- async with ZMQContextManager(
556
- zmq.DEALER,
557
- connect=self.description['database']) as socket:
558
+ async with ZMQContextManager(zmq.DEALER,
559
+ connect=self.description['database'],
560
+ socket=self._sock) as socket:
558
561
  self._sock = socket
559
562
  await self._run()
560
563
  else:
561
564
  await self._run()
562
565
 
563
566
  async def _run(self):
564
- send_msg = asyncio.create_task(self._send_msg())
567
+ send_msg_task = asyncio.create_task(self._send_msg())
565
568
  update_progress_task = asyncio.create_task(self._update_progress())
566
569
 
567
570
  self._variables = {'self': self}
@@ -596,13 +599,13 @@ class Scan():
596
599
  self.description['order'].get(-1, []),
597
600
  self.description['getters'], self.variables))
598
601
 
599
- self.emit(0, 0, 0, self.variables)
600
- self.emit(-1, 0, 0, {})
602
+ await self.emit(0, 0, 0, self.variables)
603
+ await self.emit(-1, 0, 0, {})
601
604
 
602
605
  await self._prm_queue.join()
603
606
  update_progress_task.cancel()
604
607
  await self._msg_queue.join()
605
- send_msg.cancel()
608
+ send_msg_task.cancel()
606
609
  return self.variables
607
610
 
608
611
  async def done(self):
@@ -621,13 +624,18 @@ class Scan():
621
624
 
622
625
  async def submit(self, server=default_executor):
623
626
  assymbly(self.description)
624
- async with ZMQContextManager(zmq.DEALER, connect=server) as socket:
627
+ async with ZMQContextManager(zmq.DEALER,
628
+ connect=server,
629
+ socket=self._sock) as socket:
625
630
  await socket.send_pyobj({
626
- 'method': 'submit',
631
+ 'method': 'task_submit',
627
632
  'description': dill.dumps(self.description)
628
633
  })
629
634
  self.id = await socket.recv_pyobj()
630
- await socket.send_pyobj({'method': 'get_record_id', 'id': self.id})
635
+ await socket.send_pyobj({
636
+ 'method': 'task_get_record_id',
637
+ 'id': self.id
638
+ })
631
639
  record_id = await socket.recv_pyobj()
632
640
  self.record = Record(record_id, self.description['database'],
633
641
  self.description)
@@ -661,14 +669,15 @@ class Scan():
661
669
  if await self._filter(variables, self.current_level - 1):
662
670
  yield variables
663
671
  self._single_step = False
664
- self.emit(self.current_level - 1, step, position, variables)
672
+ await self.emit(self.current_level - 1, step, position,
673
+ variables)
665
674
  step += 1
666
675
  position += 1
667
676
  self._current_level -= 1
668
677
  self._prm_queue.put_nowait(
669
678
  self._update_progress_bar(self.current_level, 1))
670
679
  if self.current_level == 0:
671
- self.emit(self.current_level - 1, 0, 0, {})
680
+ await self.emit(self.current_level - 1, 0, 0, {})
672
681
  for name, value in self.variables.items():
673
682
  if inspect.isawaitable(value):
674
683
  self.variables[name] = await value
qulab/scan/server.py CHANGED
@@ -2,6 +2,7 @@ import asyncio
2
2
  import os
3
3
  import pickle
4
4
  import time
5
+ import uuid
5
6
  from pathlib import Path
6
7
 
7
8
  import click
@@ -29,7 +30,9 @@ else:
29
30
  datapath = Path.home() / 'qulab' / 'data'
30
31
  datapath.mkdir(parents=True, exist_ok=True)
31
32
 
33
+ namespace = uuid.uuid4()
32
34
  record_cache = {}
35
+ buffer_list_cache = {}
33
36
  CACHE_SIZE = 1024
34
37
 
35
38
  pool = {}
@@ -44,6 +47,9 @@ class Request():
44
47
  self.msg = pickle.loads(msg)
45
48
  self.method = self.msg.get('method', '')
46
49
 
50
+ def __repr__(self):
51
+ return f"Request({self.method})"
52
+
47
53
 
48
54
  async def reply(req, resp):
49
55
  await req.sock.send_multipart([req.identity, pickle.dumps(resp)])
@@ -57,6 +63,11 @@ def clear_cache():
57
63
  range(len(record_cache) - CACHE_SIZE)):
58
64
  del record_cache[k]
59
65
 
66
+ for k, (t,
67
+ _) in zip(sorted(buffer_list_cache.items(), key=lambda x: x[1][0]),
68
+ range(len(buffer_list_cache) - CACHE_SIZE)):
69
+ del buffer_list_cache[k]
70
+
60
71
 
61
72
  def flush_cache():
62
73
  for k, (t, r) in record_cache.items():
@@ -142,12 +153,37 @@ async def handle(session: Session, request: Request, datapath: Path):
142
153
  match request.method:
143
154
  case 'ping':
144
155
  await reply(request, 'pong')
145
- case 'bufferlist_slice':
146
- record = get_record(session, msg['record_id'], datapath)
147
- bufferlist = record.get(msg['key'],
148
- buffer_to_array=False,
149
- slice=msg['slice'])
150
- await reply(request, list(bufferlist.iter()))
156
+ case 'bufferlist_iter':
157
+ if msg['iter_id'] in buffer_list_cache:
158
+ it = buffer_list_cache[msg['iter_id']][1]
159
+ iter_id = msg['iter_id']
160
+ else:
161
+ iter_id = uuid.uuid3(namespace, str(time.time_ns())).bytes
162
+ record = get_record(session, msg['record_id'], datapath)
163
+ bufferlist = record.get(msg['key'],
164
+ buffer_to_array=False,
165
+ slice=msg['slice'])
166
+ it = bufferlist.iter()
167
+ for _, _ in zip(range(msg['start']), it):
168
+ pass
169
+ current_time = time.time()
170
+ ret, end = [], False
171
+ while time.time() - current_time < 0.02:
172
+ try:
173
+ ret.append(next(it))
174
+ except StopIteration:
175
+ end = True
176
+ break
177
+ await reply(request, (iter_id, ret, end))
178
+ buffer_list_cache[iter_id] = time.time(), it
179
+ clear_cache()
180
+ case 'bufferlist_iter_exit':
181
+ try:
182
+ it = buffer_list_cache.pop(msg['iter_id'])[1]
183
+ it.throw(Exception)
184
+ except:
185
+ pass
186
+ clear_cache()
151
187
  case 'record_create':
152
188
  description = dill.loads(msg['description'])
153
189
  await reply(request, record_create(session, description, datapath))
@@ -220,7 +256,7 @@ async def handle(session: Session, request: Request, datapath: Path):
220
256
  'objects').parts[-4:]))
221
257
  session.commit()
222
258
  await reply(request, config.id)
223
- case 'submit':
259
+ case 'task_submit':
224
260
  from .scan import Scan
225
261
  finished = [(id, queried) for id, (task, queried) in pool.items()
226
262
  if not isinstance(task, int) and task.finished()]
@@ -235,7 +271,7 @@ async def handle(session: Session, request: Request, datapath: Path):
235
271
  task.start()
236
272
  pool[task.id] = [task, False]
237
273
  await reply(request, task.id)
238
- case 'get_record_id':
274
+ case 'task_get_record_id':
239
275
  task, queried = pool.get(msg['id'])
240
276
  if isinstance(task, int):
241
277
  await reply(request, task)
@@ -249,15 +285,28 @@ async def handle(session: Session, request: Request, datapath: Path):
249
285
  await asyncio.sleep(1)
250
286
  else:
251
287
  await reply(request, None)
288
+ case 'task_get_progress':
289
+ task, _ = pool.get(msg['id'])
290
+ if isinstance(task, int):
291
+ await reply(request, 1)
292
+ else:
293
+ await reply(request,
294
+ [(bar.n, bar.total) for bar in task._bar.values()])
252
295
  case _:
253
296
  logger.error(f"Unknown method: {msg['method']}")
254
297
 
255
298
 
256
- async def _handle(session: Session, request: Request, datapath: Path):
299
+ async def handle_with_timeout(session: Session, request: Request,
300
+ datapath: Path, timeout: float):
257
301
  try:
258
- await handle(session, request, datapath)
259
- except:
260
- await reply(request, 'error')
302
+ await asyncio.wait_for(handle(session, request, datapath),
303
+ timeout=timeout)
304
+ except asyncio.TimeoutError:
305
+ logger.warning(
306
+ f"Task handling request {request} timed out and was cancelled.")
307
+ await reply(request, 'timeout')
308
+ except Exception as e:
309
+ await reply(request, f'{e!r}')
261
310
 
262
311
 
263
312
  async def serv(port,
@@ -280,7 +329,8 @@ async def serv(port,
280
329
  identity, msg = await sock.recv_multipart()
281
330
  received += len(msg)
282
331
  req = Request(sock, identity, msg)
283
- asyncio.create_task(_handle(session, req, datapath))
332
+ asyncio.create_task(
333
+ handle_with_timeout(session, req, datapath, timeout=60.0))
284
334
  if received > buffer_size or time.time(
285
335
  ) - last_flush_time > interval:
286
336
  flush_cache()
qulab/scan/space.py CHANGED
@@ -28,6 +28,8 @@ class Space():
28
28
  return space
29
29
  if isinstance(space, (list, tuple)):
30
30
  array = np.array(space)
31
+ elif isinstance(space, np.ndarray):
32
+ array = space
31
33
  try:
32
34
  a = np.linspace(array[0], array[-1], len(array), dtype=array.dtype)
33
35
  if np.allclose(a, array):
@@ -130,7 +130,12 @@ class ZMQContextManager:
130
130
  self.auth = None
131
131
  self.context = None
132
132
  self.socket = None
133
- self._external_socket = socket
133
+ self._external_socket = None
134
+ try:
135
+ if not socket.closed:
136
+ self._external_socket = socket
137
+ except:
138
+ pass
134
139
 
135
140
  def _create_socket(self, asyncio=False) -> zmq.Socket:
136
141
  """
qulab/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.2.4"
1
+ __version__ = "2.2.6"
File without changes
File without changes