QuLab 2.3.2__cp310-cp310-win_amd64.whl → 2.3.3__cp310-cp310-win_amd64.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.
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/METADATA +1 -1
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/RECORD +12 -12
- qulab/fun.cp310-win_amd64.pyd +0 -0
- qulab/scan/expression.py +29 -1
- qulab/scan/record.py +3 -1
- qulab/scan/scan.py +70 -35
- qulab/scan/server.py +143 -32
- qulab/version.py +1 -1
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/LICENSE +0 -0
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/WHEEL +0 -0
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/entry_points.txt +0 -0
- {QuLab-2.3.2.dist-info → QuLab-2.3.3.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
qulab/__init__.py,sha256=vkFybY8YSsQilYdThPRD83-btPAR41sy_WCXiM-6mME,141
|
|
2
2
|
qulab/__main__.py,sha256=V7iokU7awstgjCeiF_hoOdFyrqJwC_4QetiLe7cWvOQ,454
|
|
3
|
-
qulab/fun.cp310-win_amd64.pyd,sha256=
|
|
4
|
-
qulab/version.py,sha256=
|
|
3
|
+
qulab/fun.cp310-win_amd64.pyd,sha256=0EVxSA6croyc_6ulRHyRYP_LlAK7b7ecgQEpDD0Pg68,31744
|
|
4
|
+
qulab/version.py,sha256=_TojZ2sN-5aiITSzim-_br5UuW9pwpo2Tx8vnDWpsqE,21
|
|
5
5
|
qulab/monitor/__init__.py,sha256=xEVDkJF8issrsDeLqQmDsvtRmrf-UiViFcGTWuzdlFU,43
|
|
6
6
|
qulab/monitor/__main__.py,sha256=k2H1H5Zf9LLXTDLISJkbikLH-z0f1e5i5i6wXXYPOrE,105
|
|
7
7
|
qulab/monitor/config.py,sha256=y_5StMkdrbZO1ziyKBrvIkB7Jclp9RCPK1QbsOhCxnY,785
|
|
@@ -14,13 +14,13 @@ qulab/monitor/qt_compat.py,sha256=Eq7zlA4_XstB92NhtAqebtWU_Btw4lcwFO30YxZ-TPE,80
|
|
|
14
14
|
qulab/monitor/toolbar.py,sha256=HxqG6ywKFyQJM2Q1s7SnhuzjbyeROczAZKwxztD1WJ8,8213
|
|
15
15
|
qulab/scan/__init__.py,sha256=RR_0NQcr8Mi3vpWdypydbijQ1rXA0D3DEidQ7xjNslM,133
|
|
16
16
|
qulab/scan/curd.py,sha256=yaTglGiS6mlk0GqDHi_w8T02XGBMvDZtXSdML7zDywk,7117
|
|
17
|
-
qulab/scan/expression.py,sha256=
|
|
17
|
+
qulab/scan/expression.py,sha256=l7TYBmcJIo0M5GJm1TtrkrMFj5mCmrfLOMCILKbOivU,20712
|
|
18
18
|
qulab/scan/models.py,sha256=ZvXkJEt5Yz3Sjx0JKzYka-q2Uo-w_iVzHgH8A6DbjF0,18236
|
|
19
19
|
qulab/scan/optimize.py,sha256=ACfGSfrFpPgmZo_P5kD4mquahXImYipdP7E86dtFQO8,2635
|
|
20
20
|
qulab/scan/query.py,sha256=RM8bG4Tcx_PaNk8tv9HdlTZ1dGuuSr3sZVkYVq2BtfQ,12183
|
|
21
|
-
qulab/scan/record.py,sha256=
|
|
22
|
-
qulab/scan/scan.py,sha256=
|
|
23
|
-
qulab/scan/server.py,sha256=
|
|
21
|
+
qulab/scan/record.py,sha256=MVmxhIzwmOju7eWxJEWsqJZlVgrDeRXGMfNvXImj7Ms,21883
|
|
22
|
+
qulab/scan/scan.py,sha256=HMeVxoaEWjOj0eBusrQ-QQUFDAc_ivoLv95XkDmOS70,39947
|
|
23
|
+
qulab/scan/server.py,sha256=em2tM_JPySnUNhusvl92ZPUGxz1mM34jFo5v2ji4uy0,19611
|
|
24
24
|
qulab/scan/space.py,sha256=S-jHaXXf12FzjajOhPsZ-mkpGzAxrFAI3tRuQrvTBKg,6274
|
|
25
25
|
qulab/scan/utils.py,sha256=30qnYvyFyctwcWxOCUpCNxXgGysA7xdIDzYbjwxGUzA,3744
|
|
26
26
|
qulab/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -78,9 +78,9 @@ qulab/visualization/plot_layout.py,sha256=yAnMONOms7_szCdng-8wPpUMPis5UnbaNNzV4K
|
|
|
78
78
|
qulab/visualization/plot_seq.py,sha256=h9D0Yl_yO64IwlvBgzMu9EBKr9gg6y8QE55gu2PfTns,2783
|
|
79
79
|
qulab/visualization/qdat.py,sha256=HubXFu4nfcA7iUzghJGle1C86G6221hicLR0b-GqhKQ,5887
|
|
80
80
|
qulab/visualization/widgets.py,sha256=HcYwdhDtLreJiYaZuN3LfofjJmZcLwjMfP5aasebgDo,3266
|
|
81
|
-
QuLab-2.3.
|
|
82
|
-
QuLab-2.3.
|
|
83
|
-
QuLab-2.3.
|
|
84
|
-
QuLab-2.3.
|
|
85
|
-
QuLab-2.3.
|
|
86
|
-
QuLab-2.3.
|
|
81
|
+
QuLab-2.3.3.dist-info/LICENSE,sha256=b4NRQ-GFVpJMT7RuExW3NwhfbrYsX7AcdB7Gudok-fs,1086
|
|
82
|
+
QuLab-2.3.3.dist-info/METADATA,sha256=jKqntTp7yF3mnpI4ItBA9tLV0zjlI-v_uRTrf_AonRk,3609
|
|
83
|
+
QuLab-2.3.3.dist-info/WHEEL,sha256=H-K32IiNZYCPWcWlZi7cxr5Zu8ogRQGVOyOK_jygsF0,101
|
|
84
|
+
QuLab-2.3.3.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
|
|
85
|
+
QuLab-2.3.3.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
|
86
|
+
QuLab-2.3.3.dist-info/RECORD,,
|
qulab/fun.cp310-win_amd64.pyd
CHANGED
|
Binary file
|
qulab/scan/expression.py
CHANGED
|
@@ -419,7 +419,7 @@ class Expression():
|
|
|
419
419
|
def is_const(self, env) -> bool:
|
|
420
420
|
return False
|
|
421
421
|
|
|
422
|
-
def value(self, env):
|
|
422
|
+
def value(self, env=_default_env):
|
|
423
423
|
if self.changed(env):
|
|
424
424
|
self.cache = self.eval(env)
|
|
425
425
|
return self.cache
|
|
@@ -612,3 +612,31 @@ class Symbol(Expression):
|
|
|
612
612
|
|
|
613
613
|
def __repr__(self) -> str:
|
|
614
614
|
return self.name
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
sin = Symbol('sin')
|
|
618
|
+
cos = Symbol('cos')
|
|
619
|
+
tan = Symbol('tan')
|
|
620
|
+
pi = Symbol('pi')
|
|
621
|
+
e = Symbol('e')
|
|
622
|
+
log = Symbol('log')
|
|
623
|
+
log2 = Symbol('log2')
|
|
624
|
+
log10 = Symbol('log10')
|
|
625
|
+
exp = Symbol('exp')
|
|
626
|
+
sqrt = Symbol('sqrt')
|
|
627
|
+
abs = Symbol('abs')
|
|
628
|
+
sinh = Symbol('sinh')
|
|
629
|
+
cosh = Symbol('cosh')
|
|
630
|
+
tanh = Symbol('tanh')
|
|
631
|
+
arcsin = Symbol('arcsin')
|
|
632
|
+
arccos = Symbol('arccos')
|
|
633
|
+
arctan = Symbol('arctan')
|
|
634
|
+
arctan2 = Symbol('arctan2')
|
|
635
|
+
arcsinh = Symbol('arcsinh')
|
|
636
|
+
arccosh = Symbol('arccosh')
|
|
637
|
+
arctanh = Symbol('arctanh')
|
|
638
|
+
sinc = Symbol('sinc')
|
|
639
|
+
sign = Symbol('sign')
|
|
640
|
+
heaviside = Symbol('heaviside')
|
|
641
|
+
erf = Symbol('erf')
|
|
642
|
+
erfc = Symbol('erfc')
|
qulab/scan/record.py
CHANGED
|
@@ -474,7 +474,9 @@ class Record():
|
|
|
474
474
|
'method': 'record_keys',
|
|
475
475
|
'record_id': self.id
|
|
476
476
|
})
|
|
477
|
-
|
|
477
|
+
keys = socket.recv_pyobj()
|
|
478
|
+
self._items = {key: None for key in keys}
|
|
479
|
+
return keys
|
|
478
480
|
else:
|
|
479
481
|
return list(self._items.keys())
|
|
480
482
|
|
qulab/scan/scan.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import copy
|
|
3
4
|
import inspect
|
|
4
5
|
import itertools
|
|
@@ -204,6 +205,19 @@ def _run_function_in_process(buf):
|
|
|
204
205
|
return func(*args, **kwds)
|
|
205
206
|
|
|
206
207
|
|
|
208
|
+
def _dump_description(description):
|
|
209
|
+
d = {}
|
|
210
|
+
for key, value in description.items():
|
|
211
|
+
if key in [
|
|
212
|
+
'intrinsic_loops', 'app', 'tags', 'loops',
|
|
213
|
+
'independent_variables', 'axis', 'config', 'entry'
|
|
214
|
+
]:
|
|
215
|
+
d[key] = value
|
|
216
|
+
else:
|
|
217
|
+
d[key] = dill.dumps(value)
|
|
218
|
+
return dill.dumps(description)
|
|
219
|
+
|
|
220
|
+
|
|
207
221
|
class Scan():
|
|
208
222
|
|
|
209
223
|
def __new__(cls, *args, mixin=None, **kwds):
|
|
@@ -331,6 +345,9 @@ class Scan():
|
|
|
331
345
|
await _unpack(key, variables)
|
|
332
346
|
elif inspect.isawaitable(value) and not self.hiden(key):
|
|
333
347
|
variables[key] = await value
|
|
348
|
+
|
|
349
|
+
if self.record is None:
|
|
350
|
+
self.record = await self.create_record()
|
|
334
351
|
if self._sock is not None:
|
|
335
352
|
await self._sock.send_pyobj({
|
|
336
353
|
'task': self.id,
|
|
@@ -374,20 +391,33 @@ class Scan():
|
|
|
374
391
|
return True
|
|
375
392
|
|
|
376
393
|
async def create_record(self):
|
|
377
|
-
if self._sock is
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
'
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
394
|
+
if self._sock is None:
|
|
395
|
+
return Record(None, self.description['database'], self.description)
|
|
396
|
+
|
|
397
|
+
if self.config:
|
|
398
|
+
self.description['config'] = await create_config(
|
|
399
|
+
self.config, self.description['database'], self._sock)
|
|
400
|
+
if current_notebook() is None:
|
|
401
|
+
await create_notebook('untitle', self.description['database'],
|
|
402
|
+
self._sock)
|
|
403
|
+
cell_id = await save_input_cells(current_notebook(),
|
|
404
|
+
self.description['entry']['scripts'],
|
|
405
|
+
self.description['database'],
|
|
406
|
+
self._sock)
|
|
407
|
+
self.description['entry']['scripts'] = cell_id
|
|
408
|
+
|
|
409
|
+
await self._sock.send_pyobj({
|
|
410
|
+
'task':
|
|
411
|
+
self.id,
|
|
412
|
+
'method':
|
|
413
|
+
'record_create',
|
|
414
|
+
'description':
|
|
415
|
+
_dump_description(self.description)
|
|
416
|
+
})
|
|
386
417
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
return Record(None, self.description['database'], self.description)
|
|
418
|
+
record_id = await self._sock.recv_pyobj()
|
|
419
|
+
return Record(record_id, self.description['database'],
|
|
420
|
+
self.description)
|
|
391
421
|
|
|
392
422
|
def get(self, name: str):
|
|
393
423
|
if name in self.description['consts']:
|
|
@@ -481,7 +511,7 @@ class Scan():
|
|
|
481
511
|
elif isinstance(space, OptimizeSpace):
|
|
482
512
|
space.name = name
|
|
483
513
|
space.optimizer.dimensions[name] = space.space
|
|
484
|
-
if space.suggestion:
|
|
514
|
+
if space.suggestion is not None:
|
|
485
515
|
space.optimizer.suggestion[name] = space.suggestion
|
|
486
516
|
self._add_search_space(name, space.optimizer.level, space)
|
|
487
517
|
self.add_depends(space.optimizer.name, [name])
|
|
@@ -584,6 +614,26 @@ class Scan():
|
|
|
584
614
|
|
|
585
615
|
async def run(self):
|
|
586
616
|
assymbly(self.description)
|
|
617
|
+
|
|
618
|
+
@contextlib.asynccontextmanager
|
|
619
|
+
async def send_msg_and_update_bar(self):
|
|
620
|
+
send_msg_task = asyncio.create_task(self._send_msg())
|
|
621
|
+
update_progress_task = asyncio.create_task(self._update_progress())
|
|
622
|
+
try:
|
|
623
|
+
yield
|
|
624
|
+
finally:
|
|
625
|
+
update_progress_task.cancel()
|
|
626
|
+
send_msg_task.cancel()
|
|
627
|
+
while True:
|
|
628
|
+
try:
|
|
629
|
+
task = self._prm_queue.get_nowait()
|
|
630
|
+
except:
|
|
631
|
+
break
|
|
632
|
+
try:
|
|
633
|
+
task.cancel()
|
|
634
|
+
except:
|
|
635
|
+
pass
|
|
636
|
+
|
|
587
637
|
if isinstance(
|
|
588
638
|
self.description['database'],
|
|
589
639
|
str) and self.description['database'].startswith("tcp://"):
|
|
@@ -591,27 +641,15 @@ class Scan():
|
|
|
591
641
|
connect=self.description['database'],
|
|
592
642
|
socket=self._sock) as socket:
|
|
593
643
|
self._sock = socket
|
|
594
|
-
|
|
595
|
-
self.
|
|
596
|
-
self.config, self.description['database'], self._sock)
|
|
597
|
-
if current_notebook() is None:
|
|
598
|
-
await create_notebook('untitle',
|
|
599
|
-
self.description['database'],
|
|
600
|
-
self._sock)
|
|
601
|
-
cell_id = await save_input_cells(
|
|
602
|
-
current_notebook(), self.description['entry']['scripts'],
|
|
603
|
-
self.description['database'], self._sock)
|
|
604
|
-
self.description['entry']['scripts'] = cell_id
|
|
605
|
-
await self._run()
|
|
644
|
+
async with send_msg_and_update_bar(self):
|
|
645
|
+
await self._run()
|
|
606
646
|
else:
|
|
607
647
|
if self.config:
|
|
608
648
|
self.description['config'] = copy.deepcopy(self.config)
|
|
609
|
-
|
|
649
|
+
async with send_msg_and_update_bar(self):
|
|
650
|
+
await self._run()
|
|
610
651
|
|
|
611
652
|
async def _run(self):
|
|
612
|
-
send_msg_task = asyncio.create_task(self._send_msg())
|
|
613
|
-
update_progress_task = asyncio.create_task(self._update_progress())
|
|
614
|
-
|
|
615
653
|
self._variables = {'self': self, 'config': self.config}
|
|
616
654
|
|
|
617
655
|
consts = {}
|
|
@@ -634,7 +672,6 @@ class Scan():
|
|
|
634
672
|
await update_variables(self.variables, updates,
|
|
635
673
|
self.description['setters'])
|
|
636
674
|
|
|
637
|
-
self.record = await self.create_record()
|
|
638
675
|
await self.work()
|
|
639
676
|
for level, bar in self._bar.items():
|
|
640
677
|
bar.close()
|
|
@@ -648,9 +685,7 @@ class Scan():
|
|
|
648
685
|
await self.emit(-1, 0, 0, {})
|
|
649
686
|
|
|
650
687
|
await self._prm_queue.join()
|
|
651
|
-
update_progress_task.cancel()
|
|
652
688
|
await self._msg_queue.join()
|
|
653
|
-
send_msg_task.cancel()
|
|
654
689
|
return self.variables
|
|
655
690
|
|
|
656
691
|
async def done(self):
|
|
@@ -776,10 +811,10 @@ class Scan():
|
|
|
776
811
|
buf = dill.dumps((awaitable, args, kwds))
|
|
777
812
|
task = asyncio.get_running_loop().run_in_executor(
|
|
778
813
|
self._executors, _run_function_in_process, buf)
|
|
779
|
-
self._prm_queue.put_nowait(task)
|
|
780
|
-
return Promise(task)
|
|
781
814
|
except:
|
|
782
815
|
return awaitable(*args, **kwds)
|
|
816
|
+
self._prm_queue.put_nowait(task)
|
|
817
|
+
return Promise(task)
|
|
783
818
|
else:
|
|
784
819
|
return awaitable
|
|
785
820
|
|
qulab/scan/server.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import os
|
|
3
3
|
import pickle
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
4
6
|
import time
|
|
5
7
|
import uuid
|
|
6
8
|
from pathlib import Path
|
|
@@ -59,33 +61,45 @@ def clear_cache():
|
|
|
59
61
|
if len(record_cache) < CACHE_SIZE:
|
|
60
62
|
return
|
|
61
63
|
|
|
64
|
+
logger.debug(f"clear_cache record_cache: {len(record_cache)}")
|
|
62
65
|
for k, (t, _) in zip(sorted(record_cache.items(), key=lambda x: x[1][0]),
|
|
63
66
|
range(len(record_cache) - CACHE_SIZE)):
|
|
64
67
|
del record_cache[k]
|
|
65
68
|
|
|
69
|
+
logger.debug(f"clear_cache buffer_list_cache: {len(buffer_list_cache)}")
|
|
66
70
|
for k, (t,
|
|
67
71
|
_) in zip(sorted(buffer_list_cache.items(), key=lambda x: x[1][0]),
|
|
68
72
|
range(len(buffer_list_cache) - CACHE_SIZE)):
|
|
69
73
|
del buffer_list_cache[k]
|
|
74
|
+
logger.debug(f"clear_cache done.")
|
|
70
75
|
|
|
71
76
|
|
|
72
77
|
def flush_cache():
|
|
78
|
+
logger.debug(f"flush_cache: {len(record_cache)}")
|
|
73
79
|
for k, (t, r) in record_cache.items():
|
|
74
80
|
r.flush()
|
|
81
|
+
logger.debug(f"flush_cache done.")
|
|
75
82
|
|
|
76
83
|
|
|
77
84
|
def get_local_record(session: Session, id: int, datapath: Path) -> Record:
|
|
85
|
+
logger.debug(f"get_local_record: {id}")
|
|
78
86
|
record_in_db = session.get(RecordInDB, id)
|
|
79
87
|
if record_in_db is None:
|
|
88
|
+
logger.debug(f"record not found: {id=}")
|
|
80
89
|
return None
|
|
81
90
|
record_in_db.atime = utcnow()
|
|
82
91
|
|
|
83
92
|
if record_in_db.file.endswith('.zip'):
|
|
84
|
-
|
|
93
|
+
logger.debug(f"load record from zip: {record_in_db.file}")
|
|
94
|
+
record = Record.load(datapath / 'objects' / record_in_db.file)
|
|
95
|
+
logger.debug(f"load record from zip done.")
|
|
96
|
+
return record
|
|
85
97
|
|
|
86
98
|
path = datapath / 'objects' / record_in_db.file
|
|
87
99
|
with open(path, 'rb') as f:
|
|
100
|
+
logger.debug(f"load record from file: {path}")
|
|
88
101
|
record = dill.load(f)
|
|
102
|
+
logger.debug(f"load record from file done.")
|
|
89
103
|
record.database = datapath
|
|
90
104
|
record._file = path
|
|
91
105
|
return record
|
|
@@ -95,13 +109,16 @@ def get_record(session: Session, id: int, datapath: Path) -> Record:
|
|
|
95
109
|
if id not in record_cache:
|
|
96
110
|
record = get_local_record(session, id, datapath)
|
|
97
111
|
else:
|
|
112
|
+
logger.debug(f"get_record from cache: {id=}")
|
|
98
113
|
record = record_cache[id][1]
|
|
99
114
|
clear_cache()
|
|
115
|
+
logger.debug(f"update lru time for record cache: {id=}")
|
|
100
116
|
record_cache[id] = time.time(), record
|
|
101
117
|
return record
|
|
102
118
|
|
|
103
119
|
|
|
104
120
|
def record_create(session: Session, description: dict, datapath: Path) -> int:
|
|
121
|
+
logger.debug(f"record_create: {description['app']}")
|
|
105
122
|
record = Record(None, datapath, description)
|
|
106
123
|
record_in_db = RecordInDB()
|
|
107
124
|
if 'app' in description:
|
|
@@ -111,28 +128,39 @@ def record_create(session: Session, description: dict, datapath: Path) -> int:
|
|
|
111
128
|
record_in_db.file = '/'.join(record._file.parts[-4:])
|
|
112
129
|
record_in_db.config_id = description['config']
|
|
113
130
|
record._file = datapath / 'objects' / record_in_db.file
|
|
131
|
+
logger.debug(f"record_create generate random file: {record_in_db.file}")
|
|
114
132
|
session.add(record_in_db)
|
|
115
133
|
try:
|
|
116
134
|
session.commit()
|
|
135
|
+
logger.debug(f"record_create commited: record.id={record_in_db.id}")
|
|
117
136
|
record.id = record_in_db.id
|
|
118
137
|
clear_cache()
|
|
119
138
|
record_cache[record.id] = time.time(), record
|
|
120
139
|
return record.id
|
|
121
140
|
except:
|
|
141
|
+
logger.debug(f"record_create rollback")
|
|
122
142
|
session.rollback()
|
|
123
143
|
raise
|
|
124
144
|
|
|
125
145
|
|
|
126
146
|
def record_append(session: Session, record_id: int, level: int, step: int,
|
|
127
147
|
position: int, variables: dict, datapath: Path):
|
|
148
|
+
logger.debug(f"record_append: {record_id}")
|
|
128
149
|
record = get_record(session, record_id, datapath)
|
|
150
|
+
logger.debug(f"record_append: {record_id}, {level}, {step}, {position}")
|
|
129
151
|
record.append(level, step, position, variables)
|
|
152
|
+
logger.debug(f"record_append done.")
|
|
130
153
|
try:
|
|
154
|
+
logger.debug(f"record_append update SQL database.")
|
|
131
155
|
record_in_db = session.get(RecordInDB, record_id)
|
|
156
|
+
logger.debug(f"record_append get RecordInDB: {record_in_db}")
|
|
132
157
|
record_in_db.mtime = utcnow()
|
|
133
158
|
record_in_db.atime = utcnow()
|
|
159
|
+
logger.debug(f"record_append update RecordInDB: {record_in_db}")
|
|
134
160
|
session.commit()
|
|
161
|
+
logger.debug(f"record_append commited.")
|
|
135
162
|
except:
|
|
163
|
+
logger.debug(f"record_append rollback.")
|
|
136
164
|
session.rollback()
|
|
137
165
|
raise
|
|
138
166
|
|
|
@@ -150,11 +178,15 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
150
178
|
|
|
151
179
|
msg = request.msg
|
|
152
180
|
|
|
181
|
+
if request.method not in ['ping']:
|
|
182
|
+
logger.debug(f"handle: {request.method}")
|
|
183
|
+
|
|
153
184
|
match request.method:
|
|
154
185
|
case 'ping':
|
|
155
186
|
await reply(request, 'pong')
|
|
156
187
|
case 'bufferlist_iter':
|
|
157
|
-
|
|
188
|
+
logger.debug(f"bufferlist_iter: {msg}")
|
|
189
|
+
if msg['iter_id'] and msg['iter_id'] in buffer_list_cache:
|
|
158
190
|
it = buffer_list_cache[msg['iter_id']][1]
|
|
159
191
|
iter_id = msg['iter_id']
|
|
160
192
|
else:
|
|
@@ -174,22 +206,30 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
174
206
|
except StopIteration:
|
|
175
207
|
end = True
|
|
176
208
|
break
|
|
209
|
+
logger.debug(f"bufferlist_iter: {iter_id}, {end}")
|
|
177
210
|
await reply(request, (iter_id, ret, end))
|
|
211
|
+
logger.debug(f"reply bufferlist_iter: {iter_id}, {end}")
|
|
178
212
|
buffer_list_cache[iter_id] = time.time(), it
|
|
179
213
|
clear_cache()
|
|
180
214
|
case 'bufferlist_iter_exit':
|
|
215
|
+
logger.debug(f"bufferlist_iter_exit: {msg}")
|
|
181
216
|
try:
|
|
182
217
|
it = buffer_list_cache.pop(msg['iter_id'])[1]
|
|
183
218
|
it.throw(Exception)
|
|
184
219
|
except:
|
|
185
220
|
pass
|
|
186
221
|
clear_cache()
|
|
222
|
+
logger.debug(f"end bufferlist_iter_exit: {msg}")
|
|
187
223
|
case 'record_create':
|
|
224
|
+
logger.debug(f"record_create")
|
|
188
225
|
description = dill.loads(msg['description'])
|
|
189
226
|
await reply(request, record_create(session, description, datapath))
|
|
227
|
+
logger.debug(f"reply record_create")
|
|
190
228
|
case 'record_append':
|
|
229
|
+
logger.debug(f"record_append")
|
|
191
230
|
record_append(session, msg['record_id'], msg['level'], msg['step'],
|
|
192
231
|
msg['position'], msg['variables'], datapath)
|
|
232
|
+
logger.debug(f"reply record_append")
|
|
193
233
|
case 'record_description':
|
|
194
234
|
record = get_record(session, msg['record_id'], datapath)
|
|
195
235
|
await reply(request, dill.dumps(record))
|
|
@@ -295,6 +335,9 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
295
335
|
case _:
|
|
296
336
|
logger.error(f"Unknown method: {msg['method']}")
|
|
297
337
|
|
|
338
|
+
if request.method not in ['ping']:
|
|
339
|
+
logger.debug(f"finished handle: {request.method}")
|
|
340
|
+
|
|
298
341
|
|
|
299
342
|
async def handle_with_timeout(session: Session, request: Request,
|
|
300
343
|
datapath: Path, timeout: float):
|
|
@@ -311,22 +354,41 @@ async def handle_with_timeout(session: Session, request: Request,
|
|
|
311
354
|
|
|
312
355
|
async def serv(port,
|
|
313
356
|
datapath,
|
|
314
|
-
url=
|
|
357
|
+
url='',
|
|
315
358
|
buffer_size=1024 * 1024 * 1024,
|
|
316
|
-
interval=60
|
|
317
|
-
|
|
359
|
+
interval=60,
|
|
360
|
+
log='stderr',
|
|
361
|
+
debug=False):
|
|
362
|
+
if debug:
|
|
363
|
+
level = 'DEBUG'
|
|
364
|
+
else:
|
|
365
|
+
level = 'INFO'
|
|
366
|
+
|
|
367
|
+
if log == 'stderr':
|
|
368
|
+
pass
|
|
369
|
+
#logger.add(sys.stderr, level=level)
|
|
370
|
+
elif log == 'stdout':
|
|
371
|
+
logger.add(sys.stdout, level=level)
|
|
372
|
+
else:
|
|
373
|
+
logger.add(log, level=level)
|
|
374
|
+
|
|
375
|
+
logger.debug('Creating socket...')
|
|
318
376
|
async with ZMQContextManager(zmq.ROUTER, bind=f"tcp://*:{port}") as sock:
|
|
319
|
-
|
|
377
|
+
logger.info(f'Server started at port {port}.')
|
|
378
|
+
logger.info(f'Data path: {datapath}.')
|
|
379
|
+
if not url or url == 'sqlite':
|
|
320
380
|
url = 'sqlite:///' + str(datapath / 'data.db')
|
|
321
381
|
engine = create_engine(url)
|
|
322
382
|
create_tables(engine)
|
|
323
383
|
Session = sessionmaker(engine)
|
|
324
384
|
with Session() as session:
|
|
325
|
-
logger.info('
|
|
385
|
+
logger.info(f'Database connected: {url}.')
|
|
326
386
|
received = 0
|
|
327
387
|
last_flush_time = time.time()
|
|
328
388
|
while True:
|
|
389
|
+
logger.debug('Waiting for request...')
|
|
329
390
|
identity, msg = await sock.recv_multipart()
|
|
391
|
+
logger.debug('Received request.')
|
|
330
392
|
received += len(msg)
|
|
331
393
|
try:
|
|
332
394
|
req = Request(sock, identity, msg)
|
|
@@ -334,7 +396,8 @@ async def serv(port,
|
|
|
334
396
|
logger.exception('bad request')
|
|
335
397
|
continue
|
|
336
398
|
asyncio.create_task(
|
|
337
|
-
handle_with_timeout(session, req, datapath,
|
|
399
|
+
handle_with_timeout(session, req, datapath,
|
|
400
|
+
timeout=3600.0))
|
|
338
401
|
if received > buffer_size or time.time(
|
|
339
402
|
) - last_flush_time > interval:
|
|
340
403
|
flush_cache()
|
|
@@ -342,46 +405,94 @@ async def serv(port,
|
|
|
342
405
|
last_flush_time = time.time()
|
|
343
406
|
|
|
344
407
|
|
|
345
|
-
async def
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
408
|
+
async def main(port,
|
|
409
|
+
datapath,
|
|
410
|
+
url,
|
|
411
|
+
timeout=1,
|
|
412
|
+
buffer=1024,
|
|
413
|
+
interval=60,
|
|
414
|
+
log='stderr',
|
|
415
|
+
no_watch=True,
|
|
416
|
+
debug=False):
|
|
417
|
+
if no_watch:
|
|
418
|
+
logger.info('Server starting...')
|
|
419
|
+
await serv(port, datapath, url, buffer * 1024 * 1024, interval, log,
|
|
420
|
+
debug)
|
|
421
|
+
else:
|
|
422
|
+
process = None
|
|
423
|
+
|
|
349
424
|
while True:
|
|
350
425
|
try:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
sock.
|
|
354
|
-
|
|
355
|
-
|
|
426
|
+
with ZMQContextManager(
|
|
427
|
+
zmq.DEALER, connect=f"tcp://127.0.0.1:{port}") as sock:
|
|
428
|
+
sock.setsockopt(zmq.LINGER, 0)
|
|
429
|
+
sock.send_pyobj({"method": "ping"})
|
|
430
|
+
logger.debug('ping.')
|
|
431
|
+
if sock.poll(int(1000 * timeout)):
|
|
432
|
+
sock.recv()
|
|
433
|
+
logger.debug('recv pong.')
|
|
434
|
+
else:
|
|
435
|
+
logger.debug('timeout.')
|
|
436
|
+
raise asyncio.TimeoutError()
|
|
356
437
|
except (zmq.error.ZMQError, asyncio.TimeoutError):
|
|
357
|
-
|
|
358
|
-
|
|
438
|
+
if process is not None:
|
|
439
|
+
logger.debug(
|
|
440
|
+
f'killing process... PID={process.pid}, returncode={process.returncode}'
|
|
441
|
+
)
|
|
442
|
+
process.kill()
|
|
443
|
+
logger.debug(
|
|
444
|
+
f'killed process. PID={process.pid}, returncode={process.returncode}'
|
|
445
|
+
)
|
|
446
|
+
cmd = [
|
|
447
|
+
sys.executable, "-m", "qulab", "server", "--port",
|
|
448
|
+
f"{port}", "--datapath", f"{datapath}", "--url", f"{url}",
|
|
449
|
+
"--timeout", f"{timeout}", "--buffer", f"{buffer}",
|
|
450
|
+
"--interval", f"{interval}", "--log", f"{log}",
|
|
451
|
+
]
|
|
452
|
+
if url:
|
|
453
|
+
cmd.extend(['--url', url])
|
|
454
|
+
if debug:
|
|
455
|
+
cmd.append('--debug')
|
|
456
|
+
cmd.append("--no-watch")
|
|
457
|
+
logger.debug(f"starting process: {' '.join(cmd)}")
|
|
458
|
+
process = subprocess.Popen(cmd,
|
|
459
|
+
stdout=subprocess.PIPE,
|
|
460
|
+
stderr=subprocess.PIPE,
|
|
461
|
+
cwd=os.getcwd())
|
|
462
|
+
logger.debug(
|
|
463
|
+
f'process started. PID={process.pid}, returncode={process.returncode}'
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Capture and log the output
|
|
467
|
+
# stdout, stderr = process.communicate(timeout=5)
|
|
468
|
+
# if stdout:
|
|
469
|
+
# logger.info(f'Server stdout: {stdout.decode()}')
|
|
470
|
+
# if stderr:
|
|
471
|
+
# logger.error(f'Server stderr: {stderr.decode()}')
|
|
472
|
+
|
|
473
|
+
await asyncio.sleep(5)
|
|
359
474
|
await asyncio.sleep(timeout)
|
|
360
475
|
|
|
361
476
|
|
|
362
|
-
async def main(port, datapath, url, timeout=1, buffer=1024, interval=60):
|
|
363
|
-
task = await watch(port=port,
|
|
364
|
-
datapath=datapath,
|
|
365
|
-
url=url,
|
|
366
|
-
timeout=timeout,
|
|
367
|
-
buffer=buffer,
|
|
368
|
-
interval=interval)
|
|
369
|
-
await task
|
|
370
|
-
|
|
371
|
-
|
|
372
477
|
@click.command()
|
|
373
478
|
@click.option('--port',
|
|
374
479
|
default=os.getenv('QULAB_RECORD_PORT', 6789),
|
|
375
480
|
help='Port of the server.')
|
|
376
481
|
@click.option('--datapath', default=datapath, help='Path of the data.')
|
|
377
|
-
@click.option('--url', default=
|
|
482
|
+
@click.option('--url', default='sqlite', help='URL of the database.')
|
|
378
483
|
@click.option('--timeout', default=1, help='Timeout of ping.')
|
|
379
484
|
@click.option('--buffer', default=1024, help='Buffer size (MB).')
|
|
380
485
|
@click.option('--interval',
|
|
381
486
|
default=60,
|
|
382
487
|
help='Interval of flush cache, in unit of second.')
|
|
383
|
-
|
|
384
|
-
|
|
488
|
+
@click.option('--log', default='stderr', help='Log file.')
|
|
489
|
+
@click.option('--no-watch', is_flag=True, help='Watch the server.')
|
|
490
|
+
@click.option('--debug', is_flag=True, help='Debug mode.')
|
|
491
|
+
def server(port, datapath, url, timeout, buffer, interval, log, no_watch,
|
|
492
|
+
debug):
|
|
493
|
+
asyncio.run(
|
|
494
|
+
main(port, Path(datapath), url, timeout, buffer, interval, log,
|
|
495
|
+
True, debug))
|
|
385
496
|
|
|
386
497
|
|
|
387
498
|
if __name__ == "__main__":
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.3.
|
|
1
|
+
__version__ = "2.3.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|