QuLab 2.3.3__cp312-cp312-win_amd64.whl → 2.3.4__cp312-cp312-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.3.dist-info → QuLab-2.3.4.dist-info}/METADATA +4 -1
- {QuLab-2.3.3.dist-info → QuLab-2.3.4.dist-info}/RECORD +11 -11
- qulab/fun.cp312-win_amd64.pyd +0 -0
- qulab/scan/scan.py +91 -43
- qulab/scan/server.py +63 -33
- qulab/scan/utils.py +40 -0
- qulab/version.py +1 -1
- {QuLab-2.3.3.dist-info → QuLab-2.3.4.dist-info}/LICENSE +0 -0
- {QuLab-2.3.3.dist-info → QuLab-2.3.4.dist-info}/WHEEL +0 -0
- {QuLab-2.3.3.dist-info → QuLab-2.3.4.dist-info}/entry_points.txt +0 -0
- {QuLab-2.3.3.dist-info → QuLab-2.3.4.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: QuLab
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.4
|
|
4
4
|
Summary: contral instruments and manage data
|
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
|
@@ -38,6 +38,9 @@ Requires-Dist: ply >=3.11
|
|
|
38
38
|
Requires-Dist: pyzmq >=25.1.0
|
|
39
39
|
Requires-Dist: scipy >=1.0.0
|
|
40
40
|
Requires-Dist: watchdog >=4.0.0
|
|
41
|
+
Provides-Extra: full
|
|
42
|
+
Requires-Dist: SQLAlchemy >=2.0.19 ; extra == 'full'
|
|
43
|
+
Requires-Dist: uvloop >=0.19.0 ; extra == 'full'
|
|
41
44
|
|
|
42
45
|
# QuLab
|
|
43
46
|
[](https://travis-ci.org/feihoo87/QuLab)
|
|
@@ -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.cp312-win_amd64.pyd,sha256=
|
|
4
|
-
qulab/version.py,sha256=
|
|
3
|
+
qulab/fun.cp312-win_amd64.pyd,sha256=cVkHz7_RIsRL4Ij6Ku9j5XNMr2PPk6iNBG1kxgNB528,32256
|
|
4
|
+
qulab/version.py,sha256=MtmOJMh3yHY9aTdRsZDgbpPfbzm9xdycdi9jjMKzYjM,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
|
|
@@ -19,10 +19,10 @@ 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
21
|
qulab/scan/record.py,sha256=MVmxhIzwmOju7eWxJEWsqJZlVgrDeRXGMfNvXImj7Ms,21883
|
|
22
|
-
qulab/scan/scan.py,sha256=
|
|
23
|
-
qulab/scan/server.py,sha256=
|
|
22
|
+
qulab/scan/scan.py,sha256=PDU0oZkwCntucOM4aNNgY22t-Ng8XChmFrhTqPqxDX8,41814
|
|
23
|
+
qulab/scan/server.py,sha256=ahdVpTKm8P2qB7E9HKM8b95HF89YlCJ5WWLmdJhMfgw,20479
|
|
24
24
|
qulab/scan/space.py,sha256=S-jHaXXf12FzjajOhPsZ-mkpGzAxrFAI3tRuQrvTBKg,6274
|
|
25
|
-
qulab/scan/utils.py,sha256=
|
|
25
|
+
qulab/scan/utils.py,sha256=j5ai6E-pK2tYEPYlL1FpgHG7q2DL1IVAa-hPPgz5SlQ,4757
|
|
26
26
|
qulab/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
qulab/storage/__main__.py,sha256=6-EjN0waX1yfcMPJXqpIr9UlrIEsSCFApm5G-ZeaPMQ,1742
|
|
28
28
|
qulab/storage/base_dataset.py,sha256=28y3-OZrqJ52p5sbirEpUgjb7hqwLLpd38KU9DCkD24,12217
|
|
@@ -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.4.dist-info/LICENSE,sha256=b4NRQ-GFVpJMT7RuExW3NwhfbrYsX7AcdB7Gudok-fs,1086
|
|
82
|
+
QuLab-2.3.4.dist-info/METADATA,sha256=t-ZisaizQKo8edK0p_-vQD8ljsuXo9cl2dg5wE-sFsU,3735
|
|
83
|
+
QuLab-2.3.4.dist-info/WHEEL,sha256=h3VxxLkZstcnkT86k1ujrti5ToMTN-RbBnzIRuTWCMs,101
|
|
84
|
+
QuLab-2.3.4.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
|
|
85
|
+
QuLab-2.3.4.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
|
86
|
+
QuLab-2.3.4.dist-info/RECORD,,
|
qulab/fun.cp312-win_amd64.pyd
CHANGED
|
Binary file
|
qulab/scan/scan.py
CHANGED
|
@@ -26,7 +26,7 @@ from .optimize import NgOptimizer
|
|
|
26
26
|
from .record import Record
|
|
27
27
|
from .server import default_record_port
|
|
28
28
|
from .space import Optimizer, OptimizeSpace, Space
|
|
29
|
-
from .utils import async_zip, call_function, dump_globals
|
|
29
|
+
from .utils import async_zip, call_function, dump_dict, dump_globals
|
|
30
30
|
|
|
31
31
|
try:
|
|
32
32
|
from tqdm.notebook import tqdm
|
|
@@ -205,19 +205,6 @@ def _run_function_in_process(buf):
|
|
|
205
205
|
return func(*args, **kwds)
|
|
206
206
|
|
|
207
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
|
-
|
|
221
208
|
class Scan():
|
|
222
209
|
|
|
223
210
|
def __new__(cls, *args, mixin=None, **kwds):
|
|
@@ -277,6 +264,7 @@ class Scan():
|
|
|
277
264
|
self._current_level = 0
|
|
278
265
|
self._variables = {}
|
|
279
266
|
self._main_task = None
|
|
267
|
+
self._background_tasks = ()
|
|
280
268
|
self._sock = None
|
|
281
269
|
self._sem = asyncio.Semaphore(max_promise + 1)
|
|
282
270
|
self._bar: dict[int, tqdm] = {}
|
|
@@ -300,6 +288,7 @@ class Scan():
|
|
|
300
288
|
del state['record']
|
|
301
289
|
del state['_sock']
|
|
302
290
|
del state['_main_task']
|
|
291
|
+
del state['_background_tasks']
|
|
303
292
|
del state['_bar']
|
|
304
293
|
del state['_msg_queue']
|
|
305
294
|
del state['_prm_queue']
|
|
@@ -312,6 +301,7 @@ class Scan():
|
|
|
312
301
|
self.record = None
|
|
313
302
|
self._sock = None
|
|
314
303
|
self._main_task = None
|
|
304
|
+
self._background_tasks = ()
|
|
315
305
|
self._bar = {}
|
|
316
306
|
self._prm_queue = asyncio.Queue()
|
|
317
307
|
self._msg_queue = asyncio.Queue(self._max_message)
|
|
@@ -412,7 +402,11 @@ class Scan():
|
|
|
412
402
|
'method':
|
|
413
403
|
'record_create',
|
|
414
404
|
'description':
|
|
415
|
-
|
|
405
|
+
dump_dict(self.description,
|
|
406
|
+
keys=[
|
|
407
|
+
'intrinsic_loops', 'app', 'tags', 'loops',
|
|
408
|
+
'independent_variables', 'axis', 'config', 'entry'
|
|
409
|
+
])
|
|
416
410
|
})
|
|
417
411
|
|
|
418
412
|
record_id = await self._sock.recv_pyobj()
|
|
@@ -612,27 +606,33 @@ class Scan():
|
|
|
612
606
|
await task
|
|
613
607
|
self._msg_queue.task_done()
|
|
614
608
|
|
|
609
|
+
@contextlib.asynccontextmanager
|
|
610
|
+
async def _send_msg_and_update_bar(self):
|
|
611
|
+
send_msg_task = asyncio.create_task(self._send_msg())
|
|
612
|
+
update_progress_task = asyncio.create_task(self._update_progress())
|
|
613
|
+
try:
|
|
614
|
+
yield (send_msg_task, update_progress_task)
|
|
615
|
+
finally:
|
|
616
|
+
update_progress_task.cancel()
|
|
617
|
+
send_msg_task.cancel()
|
|
618
|
+
while True:
|
|
619
|
+
try:
|
|
620
|
+
task = self._prm_queue.get_nowait()
|
|
621
|
+
except:
|
|
622
|
+
break
|
|
623
|
+
try:
|
|
624
|
+
task.cancel()
|
|
625
|
+
except:
|
|
626
|
+
pass
|
|
627
|
+
|
|
628
|
+
async def _check_background_tasks(self):
|
|
629
|
+
for task in self._background_tasks:
|
|
630
|
+
if task.done():
|
|
631
|
+
await task
|
|
632
|
+
|
|
615
633
|
async def run(self):
|
|
616
634
|
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
|
|
635
|
+
self._background_tasks = ()
|
|
636
636
|
|
|
637
637
|
if isinstance(
|
|
638
638
|
self.description['database'],
|
|
@@ -641,12 +641,14 @@ class Scan():
|
|
|
641
641
|
connect=self.description['database'],
|
|
642
642
|
socket=self._sock) as socket:
|
|
643
643
|
self._sock = socket
|
|
644
|
-
async with
|
|
644
|
+
async with self._send_msg_and_update_bar() as background_tasks:
|
|
645
|
+
self._background_tasks = background_tasks
|
|
645
646
|
await self._run()
|
|
646
647
|
else:
|
|
647
648
|
if self.config:
|
|
648
649
|
self.description['config'] = copy.deepcopy(self.config)
|
|
649
|
-
async with
|
|
650
|
+
async with self._send_msg_and_update_bar() as background_tasks:
|
|
651
|
+
self._background_tasks = background_tasks
|
|
650
652
|
await self._run()
|
|
651
653
|
|
|
652
654
|
async def _run(self):
|
|
@@ -671,11 +673,11 @@ class Scan():
|
|
|
671
673
|
self.description['functions'], self.variables)
|
|
672
674
|
await update_variables(self.variables, updates,
|
|
673
675
|
self.description['setters'])
|
|
674
|
-
|
|
676
|
+
await self._check_background_tasks()
|
|
675
677
|
await self.work()
|
|
676
678
|
for level, bar in self._bar.items():
|
|
677
679
|
bar.close()
|
|
678
|
-
|
|
680
|
+
await self._check_background_tasks()
|
|
679
681
|
if self._single_step:
|
|
680
682
|
self.variables.update(await call_many_functions(
|
|
681
683
|
self.description['order'].get(-1, []),
|
|
@@ -683,7 +685,7 @@ class Scan():
|
|
|
683
685
|
|
|
684
686
|
await self.emit(0, 0, 0, self.variables)
|
|
685
687
|
await self.emit(-1, 0, 0, {})
|
|
686
|
-
|
|
688
|
+
await self._check_background_tasks()
|
|
687
689
|
await self._prm_queue.join()
|
|
688
690
|
await self._msg_queue.join()
|
|
689
691
|
return self.variables
|
|
@@ -747,6 +749,7 @@ class Scan():
|
|
|
747
749
|
| {'config': self._synchronize_config},
|
|
748
750
|
self.description['optimizers'], self.description['setters'],
|
|
749
751
|
self.description['getters']):
|
|
752
|
+
await self._check_background_tasks()
|
|
750
753
|
self._current_level += 1
|
|
751
754
|
if await self._filter(variables, self.current_level - 1):
|
|
752
755
|
yield variables
|
|
@@ -758,11 +761,13 @@ class Scan():
|
|
|
758
761
|
self._current_level -= 1
|
|
759
762
|
self._prm_queue.put_nowait(
|
|
760
763
|
self._update_progress_bar(self.current_level, 1))
|
|
764
|
+
await self._check_background_tasks()
|
|
761
765
|
if self.current_level == 0:
|
|
762
766
|
await self.emit(self.current_level - 1, 0, 0, {})
|
|
763
767
|
for name, value in self.variables.items():
|
|
764
768
|
if inspect.isawaitable(value):
|
|
765
769
|
self.variables[name] = await value
|
|
770
|
+
await self._check_background_tasks()
|
|
766
771
|
await self._prm_queue.join()
|
|
767
772
|
|
|
768
773
|
async def work(self, **kwds):
|
|
@@ -823,7 +828,7 @@ class Scan():
|
|
|
823
828
|
return await awaitable
|
|
824
829
|
|
|
825
830
|
|
|
826
|
-
def
|
|
831
|
+
def _get_environment(description):
|
|
827
832
|
import __main__
|
|
828
833
|
from IPython import get_ipython
|
|
829
834
|
|
|
@@ -853,6 +858,10 @@ def assymbly(description):
|
|
|
853
858
|
|
|
854
859
|
description['entry']['env'] = {k: v for k, v in os.environ.items()}
|
|
855
860
|
|
|
861
|
+
return description
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def _mapping_levels(description):
|
|
856
865
|
mapping = {
|
|
857
866
|
label: level
|
|
858
867
|
for level, label in enumerate(
|
|
@@ -885,8 +894,23 @@ def assymbly(description):
|
|
|
885
894
|
len(space))
|
|
886
895
|
except:
|
|
887
896
|
pass
|
|
897
|
+
return levels
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
def _get_independent_variables(description):
|
|
901
|
+
independent_variables = set(description['intrinsic_loops'].keys())
|
|
902
|
+
for level, loops in description['loops'].items():
|
|
903
|
+
for name, iterable in loops:
|
|
904
|
+
if isinstance(iterable, (np.ndarray, list, tuple, range, Space)):
|
|
905
|
+
independent_variables.add(name)
|
|
906
|
+
return independent_variables
|
|
907
|
+
|
|
888
908
|
|
|
909
|
+
def _build_dependents(description, levels, independent_variables):
|
|
889
910
|
dependents = copy.deepcopy(description['dependents'])
|
|
911
|
+
all_nodes = set(description['dependents'].keys())
|
|
912
|
+
for key, deps in dependents.items():
|
|
913
|
+
all_nodes.update(deps)
|
|
890
914
|
|
|
891
915
|
for level in levels:
|
|
892
916
|
range_list = description['loops'].get(level, [])
|
|
@@ -899,6 +923,13 @@ def assymbly(description):
|
|
|
899
923
|
dependents[name] = set()
|
|
900
924
|
dependents[name].add(f'#__loop_{level}')
|
|
901
925
|
|
|
926
|
+
after_yield = set()
|
|
927
|
+
for key in all_nodes:
|
|
928
|
+
if key not in independent_variables and key not in description[
|
|
929
|
+
'consts']:
|
|
930
|
+
if key not in dependents:
|
|
931
|
+
after_yield.add(key)
|
|
932
|
+
|
|
902
933
|
def _get_all_depends(key, graph):
|
|
903
934
|
ret = set()
|
|
904
935
|
if key not in graph:
|
|
@@ -912,7 +943,13 @@ def assymbly(description):
|
|
|
912
943
|
full_depends = {}
|
|
913
944
|
for key in dependents:
|
|
914
945
|
full_depends[key] = _get_all_depends(key, dependents)
|
|
946
|
+
if full_depends[key] & after_yield:
|
|
947
|
+
after_yield.add(key)
|
|
948
|
+
|
|
949
|
+
return dependents, full_depends, after_yield
|
|
915
950
|
|
|
951
|
+
|
|
952
|
+
def _build_order(description, levels, dependents, full_depends):
|
|
916
953
|
levels = {}
|
|
917
954
|
passed = set()
|
|
918
955
|
all_keys = set(description['consts'].keys())
|
|
@@ -957,8 +994,9 @@ def assymbly(description):
|
|
|
957
994
|
description['order'][level].append(ready)
|
|
958
995
|
keys -= set(ready)
|
|
959
996
|
|
|
997
|
+
|
|
998
|
+
def _make_axis(description):
|
|
960
999
|
axis = {}
|
|
961
|
-
independent_variables = set(description['intrinsic_loops'].keys())
|
|
962
1000
|
|
|
963
1001
|
for name in description['consts']:
|
|
964
1002
|
axis[name] = ()
|
|
@@ -967,8 +1005,6 @@ def assymbly(description):
|
|
|
967
1005
|
if isinstance(iterable, OptimizeSpace):
|
|
968
1006
|
axis[name] = tuple(range(level + 1))
|
|
969
1007
|
continue
|
|
970
|
-
elif isinstance(iterable, (np.ndarray, list, tuple, range, Space)):
|
|
971
|
-
independent_variables.add(name)
|
|
972
1008
|
axis[name] = (level, )
|
|
973
1009
|
|
|
974
1010
|
for level, group in description['order'].items():
|
|
@@ -989,8 +1025,20 @@ def assymbly(description):
|
|
|
989
1025
|
k: tuple([x for x in v if x >= 0])
|
|
990
1026
|
for k, v in axis.items()
|
|
991
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def assymbly(description):
|
|
1031
|
+
_get_environment(description)
|
|
1032
|
+
levels = _mapping_levels(description)
|
|
1033
|
+
independent_variables = _get_independent_variables(description)
|
|
992
1034
|
description['independent_variables'] = independent_variables
|
|
993
1035
|
|
|
1036
|
+
dependents, full_depends, after_yield = _build_dependents(
|
|
1037
|
+
description, levels, independent_variables)
|
|
1038
|
+
|
|
1039
|
+
_build_order(description, levels, dependents, full_depends)
|
|
1040
|
+
_make_axis(description)
|
|
1041
|
+
|
|
994
1042
|
return description
|
|
995
1043
|
|
|
996
1044
|
|
qulab/scan/server.py
CHANGED
|
@@ -20,6 +20,7 @@ from .models import Cell, Notebook
|
|
|
20
20
|
from .models import Record as RecordInDB
|
|
21
21
|
from .models import Session, create_engine, create_tables, sessionmaker, utcnow
|
|
22
22
|
from .record import BufferList, Record, random_path
|
|
23
|
+
from .utils import dump_dict, load_dict
|
|
23
24
|
|
|
24
25
|
try:
|
|
25
26
|
default_record_port = int(os.getenv('QULAB_RECORD_PORT', 6789))
|
|
@@ -53,6 +54,16 @@ class Request():
|
|
|
53
54
|
return f"Request({self.method})"
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
class Response():
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ErrorResponse(Response):
|
|
62
|
+
|
|
63
|
+
def __init__(self, error):
|
|
64
|
+
self.error = error
|
|
65
|
+
|
|
66
|
+
|
|
56
67
|
async def reply(req, resp):
|
|
57
68
|
await req.sock.send_multipart([req.identity, pickle.dumps(resp)])
|
|
58
69
|
|
|
@@ -173,7 +184,7 @@ def record_delete(session: Session, record_id: int, datapath: Path):
|
|
|
173
184
|
session.commit()
|
|
174
185
|
|
|
175
186
|
|
|
176
|
-
@logger.catch
|
|
187
|
+
@logger.catch(reraise=True)
|
|
177
188
|
async def handle(session: Session, request: Request, datapath: Path):
|
|
178
189
|
|
|
179
190
|
msg = request.msg
|
|
@@ -222,7 +233,7 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
222
233
|
logger.debug(f"end bufferlist_iter_exit: {msg}")
|
|
223
234
|
case 'record_create':
|
|
224
235
|
logger.debug(f"record_create")
|
|
225
|
-
description =
|
|
236
|
+
description = load_dict(msg['description'])
|
|
226
237
|
await reply(request, record_create(session, description, datapath))
|
|
227
238
|
logger.debug(f"reply record_create")
|
|
228
239
|
case 'record_append':
|
|
@@ -349,29 +360,16 @@ async def handle_with_timeout(session: Session, request: Request,
|
|
|
349
360
|
f"Task handling request {request} timed out and was cancelled.")
|
|
350
361
|
await reply(request, 'timeout')
|
|
351
362
|
except Exception as e:
|
|
352
|
-
|
|
363
|
+
logger.error(f"Task handling request {request} failed: {e!r}")
|
|
364
|
+
await reply(request, ErrorResponse(f'{e!r}'))
|
|
365
|
+
logger.debug(f"Task handling request {request} finished.")
|
|
353
366
|
|
|
354
367
|
|
|
355
368
|
async def serv(port,
|
|
356
369
|
datapath,
|
|
357
370
|
url='',
|
|
358
371
|
buffer_size=1024 * 1024 * 1024,
|
|
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
|
-
|
|
372
|
+
interval=60):
|
|
375
373
|
logger.debug('Creating socket...')
|
|
376
374
|
async with ZMQContextManager(zmq.ROUTER, bind=f"tcp://*:{port}") as sock:
|
|
377
375
|
logger.info(f'Server started at port {port}.')
|
|
@@ -392,8 +390,11 @@ async def serv(port,
|
|
|
392
390
|
received += len(msg)
|
|
393
391
|
try:
|
|
394
392
|
req = Request(sock, identity, msg)
|
|
395
|
-
except:
|
|
393
|
+
except Exception as e:
|
|
396
394
|
logger.exception('bad request')
|
|
395
|
+
await sock.send_multipart(
|
|
396
|
+
[identity,
|
|
397
|
+
pickle.dumps(ErrorResponse(f'{e!r}'))])
|
|
397
398
|
continue
|
|
398
399
|
asyncio.create_task(
|
|
399
400
|
handle_with_timeout(session, req, datapath,
|
|
@@ -415,9 +416,21 @@ async def main(port,
|
|
|
415
416
|
no_watch=True,
|
|
416
417
|
debug=False):
|
|
417
418
|
if no_watch:
|
|
419
|
+
logger.remove()
|
|
420
|
+
if debug:
|
|
421
|
+
level = 'DEBUG'
|
|
422
|
+
else:
|
|
423
|
+
level = 'INFO'
|
|
424
|
+
if log == 'stderr':
|
|
425
|
+
logger.add(sys.stderr, level=level)
|
|
426
|
+
elif log == 'stdout':
|
|
427
|
+
logger.add(sys.stdout, level=level)
|
|
428
|
+
else:
|
|
429
|
+
logger.add(sys.stderr, level=level)
|
|
430
|
+
logger.add(log, level=level)
|
|
431
|
+
logger.debug(f"logging level: {level}")
|
|
418
432
|
logger.info('Server starting...')
|
|
419
|
-
await serv(port, datapath, url, buffer * 1024 * 1024, interval
|
|
420
|
-
debug)
|
|
433
|
+
await serv(port, datapath, url, buffer * 1024 * 1024, interval)
|
|
421
434
|
else:
|
|
422
435
|
process = None
|
|
423
436
|
|
|
@@ -444,10 +457,24 @@ async def main(port,
|
|
|
444
457
|
f'killed process. PID={process.pid}, returncode={process.returncode}'
|
|
445
458
|
)
|
|
446
459
|
cmd = [
|
|
447
|
-
sys.executable,
|
|
448
|
-
|
|
449
|
-
"
|
|
450
|
-
"
|
|
460
|
+
sys.executable,
|
|
461
|
+
"-m",
|
|
462
|
+
"qulab",
|
|
463
|
+
"server",
|
|
464
|
+
"--port",
|
|
465
|
+
f"{port}",
|
|
466
|
+
"--datapath",
|
|
467
|
+
f"{datapath}",
|
|
468
|
+
"--url",
|
|
469
|
+
f"{url}",
|
|
470
|
+
"--timeout",
|
|
471
|
+
f"{timeout}",
|
|
472
|
+
"--buffer",
|
|
473
|
+
f"{buffer}",
|
|
474
|
+
"--interval",
|
|
475
|
+
f"{interval}",
|
|
476
|
+
"--log",
|
|
477
|
+
f"{log}",
|
|
451
478
|
]
|
|
452
479
|
if url:
|
|
453
480
|
cmd.extend(['--url', url])
|
|
@@ -455,10 +482,7 @@ async def main(port,
|
|
|
455
482
|
cmd.append('--debug')
|
|
456
483
|
cmd.append("--no-watch")
|
|
457
484
|
logger.debug(f"starting process: {' '.join(cmd)}")
|
|
458
|
-
process = subprocess.Popen(cmd,
|
|
459
|
-
stdout=subprocess.PIPE,
|
|
460
|
-
stderr=subprocess.PIPE,
|
|
461
|
-
cwd=os.getcwd())
|
|
485
|
+
process = subprocess.Popen(cmd, cwd=os.getcwd())
|
|
462
486
|
logger.debug(
|
|
463
487
|
f'process started. PID={process.pid}, returncode={process.returncode}'
|
|
464
488
|
)
|
|
@@ -490,9 +514,15 @@ async def main(port,
|
|
|
490
514
|
@click.option('--debug', is_flag=True, help='Debug mode.')
|
|
491
515
|
def server(port, datapath, url, timeout, buffer, interval, log, no_watch,
|
|
492
516
|
debug):
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
517
|
+
try:
|
|
518
|
+
import uvloop
|
|
519
|
+
uvloop.run(
|
|
520
|
+
main(port, Path(datapath), url, timeout, buffer, interval, log,
|
|
521
|
+
True, debug))
|
|
522
|
+
except ImportError:
|
|
523
|
+
asyncio.run(
|
|
524
|
+
main(port, Path(datapath), url, timeout, buffer, interval, log,
|
|
525
|
+
True, debug))
|
|
496
526
|
|
|
497
527
|
|
|
498
528
|
if __name__ == "__main__":
|
qulab/scan/utils.py
CHANGED
|
@@ -29,6 +29,46 @@ class TooLarge:
|
|
|
29
29
|
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def dump_dict(d, keys=[]):
|
|
33
|
+
ret = {}
|
|
34
|
+
|
|
35
|
+
for key, value in d.items():
|
|
36
|
+
if key in keys:
|
|
37
|
+
ret[key] = value
|
|
38
|
+
continue
|
|
39
|
+
if isinstance(value, dict) and isinstance(key, str):
|
|
40
|
+
ret[key] = dump_dict(value,
|
|
41
|
+
keys=[
|
|
42
|
+
k[len(key) + 1:] for k in keys
|
|
43
|
+
if k.startswith(f'{key}.')
|
|
44
|
+
])
|
|
45
|
+
else:
|
|
46
|
+
try:
|
|
47
|
+
ret[key] = dill.dumps(value)
|
|
48
|
+
except:
|
|
49
|
+
ret[key] = Unpicklable(value)
|
|
50
|
+
|
|
51
|
+
return dill.dumps(ret)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_dict(buff):
|
|
55
|
+
if isinstance(buff, dict):
|
|
56
|
+
return {key: load_dict(value) for key, value in buff.items()}
|
|
57
|
+
|
|
58
|
+
if not isinstance(buff, bytes):
|
|
59
|
+
return buff
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
ret = dill.loads(buff)
|
|
63
|
+
except:
|
|
64
|
+
return buff
|
|
65
|
+
|
|
66
|
+
if isinstance(ret, dict):
|
|
67
|
+
return load_dict(ret)
|
|
68
|
+
else:
|
|
69
|
+
return ret
|
|
70
|
+
|
|
71
|
+
|
|
32
72
|
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
|
33
73
|
import __main__
|
|
34
74
|
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.3.
|
|
1
|
+
__version__ = "2.3.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|