QuLab 2.10.1__tar.gz → 2.10.3__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 (118) hide show
  1. {qulab-2.10.1 → qulab-2.10.3}/PKG-INFO +1 -1
  2. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/PKG-INFO +1 -1
  3. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/cli.py +130 -46
  4. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/load.py +66 -33
  5. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/schedule.py +55 -22
  6. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/utils.py +7 -7
  7. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/device/basedevice.py +24 -8
  8. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/drivers/FakeInstrument.py +5 -5
  9. qulab-2.10.3/qulab/version.py +1 -0
  10. qulab-2.10.1/qulab/version.py +0 -1
  11. {qulab-2.10.1 → qulab-2.10.3}/LICENSE +0 -0
  12. {qulab-2.10.1 → qulab-2.10.3}/MANIFEST.in +0 -0
  13. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/SOURCES.txt +0 -0
  14. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/dependency_links.txt +0 -0
  15. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/entry_points.txt +0 -0
  16. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/requires.txt +0 -0
  17. {qulab-2.10.1 → qulab-2.10.3}/QuLab.egg-info/top_level.txt +0 -0
  18. {qulab-2.10.1 → qulab-2.10.3}/README.md +0 -0
  19. {qulab-2.10.1 → qulab-2.10.3}/pyproject.toml +0 -0
  20. {qulab-2.10.1 → qulab-2.10.3}/qulab/__init__.py +0 -0
  21. {qulab-2.10.1 → qulab-2.10.3}/qulab/__main__.py +0 -0
  22. {qulab-2.10.1 → qulab-2.10.3}/qulab/cli/__init__.py +0 -0
  23. {qulab-2.10.1 → qulab-2.10.3}/qulab/cli/commands.py +0 -0
  24. {qulab-2.10.1 → qulab-2.10.3}/qulab/cli/config.py +0 -0
  25. {qulab-2.10.1 → qulab-2.10.3}/qulab/cli/decorators.py +0 -0
  26. {qulab-2.10.1 → qulab-2.10.3}/qulab/dicttree.py +0 -0
  27. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/__init__.py +0 -0
  28. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/analyze.py +0 -0
  29. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/storage.py +0 -0
  30. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/template.py +0 -0
  31. {qulab-2.10.1 → qulab-2.10.3}/qulab/executor/transform.py +0 -0
  32. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/__init__.py +0 -0
  33. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/__main__.py +0 -0
  34. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/config.py +0 -0
  35. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/dataset.py +0 -0
  36. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/event_queue.py +0 -0
  37. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/mainwindow.py +0 -0
  38. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/monitor.py +0 -0
  39. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/ploter.py +0 -0
  40. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/qt_compat.py +0 -0
  41. {qulab-2.10.1 → qulab-2.10.3}/qulab/monitor/toolbar.py +0 -0
  42. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/__init__.py +0 -0
  43. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/curd.py +0 -0
  44. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/expression.py +0 -0
  45. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/models.py +0 -0
  46. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/optimize.py +0 -0
  47. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/query.py +0 -0
  48. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/record.py +0 -0
  49. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/scan.py +0 -0
  50. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/server.py +0 -0
  51. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/space.py +0 -0
  52. {qulab-2.10.1 → qulab-2.10.3}/qulab/scan/utils.py +0 -0
  53. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/__init__.py +0 -0
  54. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/__main__.py +0 -0
  55. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/backend/__init__.py +0 -0
  56. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/backend/redis.py +0 -0
  57. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/base_dataset.py +0 -0
  58. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/chunk.py +0 -0
  59. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/dataset.py +0 -0
  60. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/file.py +0 -0
  61. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/__init__.py +0 -0
  62. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/base.py +0 -0
  63. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/config.py +0 -0
  64. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/file.py +0 -0
  65. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/ipy.py +0 -0
  66. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/models.py +0 -0
  67. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/record.py +0 -0
  68. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/report.py +0 -0
  69. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/models/tag.py +0 -0
  70. {qulab-2.10.1 → qulab-2.10.3}/qulab/storage/storage.py +0 -0
  71. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/__init__.py +0 -0
  72. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/chat.py +0 -0
  73. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/device/__init__.py +0 -0
  74. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/device/loader.py +0 -0
  75. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/device/utils.py +0 -0
  76. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/drivers/__init__.py +0 -0
  77. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/ipy_events.py +0 -0
  78. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/__init__.py +0 -0
  79. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/bencoder.py +0 -0
  80. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/cli.py +0 -0
  81. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/dhcp.py +0 -0
  82. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/dhcpd.py +0 -0
  83. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/kad.py +0 -0
  84. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/kcp.py +0 -0
  85. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/net/nginx.py +0 -0
  86. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/progress.py +0 -0
  87. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/__init__.py +0 -0
  88. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/client.py +0 -0
  89. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/exceptions.py +0 -0
  90. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/msgpack.py +0 -0
  91. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/msgpack.pyi +0 -0
  92. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/router.py +0 -0
  93. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/rpc.py +0 -0
  94. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/serialize.py +0 -0
  95. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/server.py +0 -0
  96. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/socket.py +0 -0
  97. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/utils.py +0 -0
  98. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/worker.py +0 -0
  99. {qulab-2.10.1 → qulab-2.10.3}/qulab/sys/rpc/zmq_socket.py +0 -0
  100. {qulab-2.10.1 → qulab-2.10.3}/qulab/tools/__init__.py +0 -0
  101. {qulab-2.10.1 → qulab-2.10.3}/qulab/tools/connection_helper.py +0 -0
  102. {qulab-2.10.1 → qulab-2.10.3}/qulab/typing.py +0 -0
  103. {qulab-2.10.1 → qulab-2.10.3}/qulab/utils.py +0 -0
  104. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/__init__.py +0 -0
  105. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/__main__.py +0 -0
  106. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/_autoplot.py +0 -0
  107. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/plot_circ.py +0 -0
  108. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/plot_layout.py +0 -0
  109. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/plot_seq.py +0 -0
  110. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/qdat.py +0 -0
  111. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/rot3d.py +0 -0
  112. {qulab-2.10.1 → qulab-2.10.3}/qulab/visualization/widgets.py +0 -0
  113. {qulab-2.10.1 → qulab-2.10.3}/setup.cfg +0 -0
  114. {qulab-2.10.1 → qulab-2.10.3}/setup.py +0 -0
  115. {qulab-2.10.1 → qulab-2.10.3}/src/qulab.h +0 -0
  116. {qulab-2.10.1 → qulab-2.10.3}/tests/test_expression.py +0 -0
  117. {qulab-2.10.1 → qulab-2.10.3}/tests/test_kad.py +0 -0
  118. {qulab-2.10.1 → qulab-2.10.3}/tests/test_scan.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuLab
3
- Version: 2.10.1
3
+ Version: 2.10.3
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.4
2
2
  Name: QuLab
3
- Version: 2.10.1
3
+ Version: 2.10.3
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -30,11 +30,16 @@ def boot(script_path):
30
30
 
31
31
 
32
32
  @logger.catch(reraise=True)
33
- def check_toplogy(workflow: WorkflowType, code_path: str | Path) -> dict:
33
+ def check_toplogy(workflow: WorkflowType,
34
+ code_path: str | Path,
35
+ veryfy_source_code: bool = True) -> dict:
34
36
  graph = {}
35
37
  try:
36
- graphlib.TopologicalSorter(make_graph(workflow, graph,
37
- code_path)).static_order()
38
+ graphlib.TopologicalSorter(
39
+ make_graph(workflow,
40
+ graph,
41
+ code_path,
42
+ veryfy_source_code=veryfy_source_code)).static_order()
38
43
  except graphlib.CycleError as e:
39
44
  logger.error(
40
45
  f"Workflow {workflow.__workflow_id__} has a circular dependency: {e}"
@@ -164,10 +169,26 @@ def get(key, api):
164
169
  help='Do not run dependents.')
165
170
  @click.option('--retry', '-r', default=1, type=int, help='Retry times.')
166
171
  @click.option('--freeze', is_flag=True, help='Freeze the config table.')
172
+ @click.option('--fail-fast',
173
+ '-f',
174
+ is_flag=True,
175
+ help='Fail immediately on first error.')
176
+ @click.option('--veryfy-source-code',
177
+ is_flag=True,
178
+ help='Veryfy the source code.')
167
179
  @log_options
168
180
  @command_option('run')
169
181
  @async_command
170
- async def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
182
+ async def run(workflow,
183
+ code,
184
+ data,
185
+ api,
186
+ plot,
187
+ no_dependents,
188
+ retry,
189
+ freeze,
190
+ fail_fast,
191
+ veryfy_source_code=True):
171
192
  """
172
193
  Run a workflow.
173
194
 
@@ -198,41 +219,73 @@ async def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
198
219
  code = Path(os.path.expanduser(code))
199
220
  data = Path(os.path.expanduser(data))
200
221
 
201
- wf = load_workflow(workflow, code)
202
- check_toplogy(wf, code)
222
+ wf = load_workflow(workflow, code, veryfy_source_code=veryfy_source_code)
223
+ check_toplogy(wf, code, veryfy_source_code=veryfy_source_code)
203
224
 
204
225
  for i in range(retry):
205
226
  try:
206
227
  if no_dependents:
207
228
  if hasattr(wf, 'entries'):
208
- for entry in get_entries(wf, code):
209
- await run_workflow(entry,
210
- code,
211
- data,
212
- plot=plot,
213
- freeze=freeze)
229
+ exceptions = []
230
+ for entry in get_entries(
231
+ wf, code, veryfy_source_code=veryfy_source_code):
232
+ try:
233
+ await run_workflow(
234
+ entry,
235
+ code,
236
+ data,
237
+ plot=plot,
238
+ freeze=freeze,
239
+ veryfy_source_code=veryfy_source_code,
240
+ )
241
+ except Exception as e:
242
+ if fail_fast:
243
+ raise e
244
+ exceptions.append(e)
245
+ if any(exceptions):
246
+ raise exceptions[0]
214
247
  else:
215
- await run_workflow(wf,
216
- code,
217
- data,
218
- plot=plot,
219
- freeze=freeze)
248
+ await run_workflow(
249
+ wf,
250
+ code,
251
+ data,
252
+ plot=plot,
253
+ freeze=freeze,
254
+ veryfy_source_code=veryfy_source_code,
255
+ )
220
256
  else:
221
257
  if hasattr(wf, 'entries'):
222
- for entry in get_entries(wf, code):
223
- await maintain_workflow(entry,
224
- code,
225
- data,
226
- run=True,
227
- plot=plot,
228
- freeze=freeze)
258
+ exceptions = []
259
+ for entry in get_entries(
260
+ wf, code, veryfy_source_code=veryfy_source_code):
261
+ try:
262
+ await maintain_workflow(
263
+ entry,
264
+ code,
265
+ data,
266
+ run=True,
267
+ plot=plot,
268
+ freeze=freeze,
269
+ fail_fast=fail_fast,
270
+ veryfy_source_code=veryfy_source_code,
271
+ )
272
+ except Exception as e:
273
+ if fail_fast:
274
+ raise e
275
+ exceptions.append(e)
276
+ if any(exceptions):
277
+ raise exceptions[0]
229
278
  else:
230
- await maintain_workflow(wf,
231
- code,
232
- data,
233
- run=True,
234
- plot=plot,
235
- freeze=freeze)
279
+ await maintain_workflow(
280
+ wf,
281
+ code,
282
+ data,
283
+ run=True,
284
+ plot=plot,
285
+ freeze=freeze,
286
+ fail_fast=fail_fast,
287
+ veryfy_source_code=veryfy_source_code,
288
+ )
236
289
  break
237
290
  except CalibrationFailedError as e:
238
291
  if i == retry - 1:
@@ -245,10 +298,24 @@ async def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
245
298
  @click.argument('workflow')
246
299
  @click.option('--retry', '-r', default=1, type=int, help='Retry times.')
247
300
  @click.option('--plot', '-p', is_flag=True, help='Plot the report.')
301
+ @click.option('--fail-fast',
302
+ '-f',
303
+ is_flag=True,
304
+ help='Fail immediately on first error.')
305
+ @click.option('--veryfy-source-code',
306
+ is_flag=True,
307
+ help='Veryfy the source code.')
248
308
  @log_options
249
309
  @command_option('maintain')
250
310
  @async_command
251
- async def maintain(workflow, code, data, api, retry, plot):
311
+ async def maintain(workflow,
312
+ code,
313
+ data,
314
+ api,
315
+ retry,
316
+ plot,
317
+ fail_fast,
318
+ veryfy_source_code=True):
252
319
  """
253
320
  Maintain a workflow.
254
321
 
@@ -275,26 +342,43 @@ async def maintain(workflow, code, data, api, retry, plot):
275
342
  code = Path(os.path.expanduser(code))
276
343
  data = Path(os.path.expanduser(data))
277
344
 
278
- wf = load_workflow(workflow, code)
279
- check_toplogy(wf, code)
345
+ wf = load_workflow(workflow, code, veryfy_source_code=veryfy_source_code)
346
+ check_toplogy(wf, code, veryfy_source_code=veryfy_source_code)
280
347
 
281
348
  for i in range(retry):
282
349
  try:
283
350
  if hasattr(wf, 'entries'):
284
- for entry in get_entries(wf, code):
285
- await maintain_workflow(entry,
286
- code,
287
- data,
288
- run=False,
289
- plot=plot,
290
- freeze=False)
351
+ exceptions = []
352
+ for entry in get_entries(
353
+ wf, code, veryfy_source_code=veryfy_source_code):
354
+ try:
355
+ await maintain_workflow(
356
+ entry,
357
+ code,
358
+ data,
359
+ run=False,
360
+ plot=plot,
361
+ freeze=False,
362
+ fail_fast=fail_fast,
363
+ veryfy_source_code=veryfy_source_code,
364
+ )
365
+ except Exception as e:
366
+ if fail_fast:
367
+ raise e
368
+ exceptions.append(e)
369
+ if any(exceptions):
370
+ raise exceptions[0]
291
371
  else:
292
- await maintain_workflow(wf,
293
- code,
294
- data,
295
- run=False,
296
- plot=plot,
297
- freeze=False)
372
+ await maintain_workflow(
373
+ wf,
374
+ code,
375
+ data,
376
+ run=False,
377
+ plot=plot,
378
+ freeze=False,
379
+ fail_fast=fail_fast,
380
+ veryfy_source_code=veryfy_source_code,
381
+ )
298
382
  break
299
383
  except CalibrationFailedError as e:
300
384
  if i == retry - 1:
@@ -1,6 +1,9 @@
1
1
  import atexit
2
2
  import inspect
3
3
  import pickle
4
+ import os
5
+ import time
6
+ import shutil
4
7
  import tempfile
5
8
  import warnings
6
9
  from importlib.util import module_from_spec, spec_from_file_location
@@ -305,7 +308,8 @@ def find_unreferenced_workflows(path: str) -> list[str]:
305
308
 
306
309
  def load_workflow_from_file(file_name: str,
307
310
  base_path: str | Path,
308
- package='workflows') -> WorkflowType:
311
+ package='workflows',
312
+ veryfy_source_code: bool = True) -> WorkflowType:
309
313
  base_path = Path(base_path)
310
314
  path = Path(file_name)
311
315
  if not (base_path / path).exists():
@@ -319,7 +323,8 @@ def load_workflow_from_file(file_name: str,
319
323
  module.__source__ = source_code
320
324
 
321
325
  if hasattr(module, 'entries'):
322
- verify_entries(module, base_path)
326
+ if veryfy_source_code:
327
+ verify_entries(module, base_path)
323
328
  return module
324
329
 
325
330
  if not hasattr(module, '__timeout__'):
@@ -327,9 +332,11 @@ def load_workflow_from_file(file_name: str,
327
332
 
328
333
  if not hasattr(module, 'depends'):
329
334
  module.depends = lambda: []
330
- verify_depends(module, base_path)
331
- verify_calibrate_method(module)
332
- verify_check_method(module)
335
+
336
+ if veryfy_source_code:
337
+ verify_depends(module, base_path)
338
+ verify_calibrate_method(module)
339
+ verify_check_method(module)
333
340
 
334
341
  return module
335
342
 
@@ -380,12 +387,14 @@ def _generate_target_file_path(template_path: str | Path, hash_str: str,
380
387
  return Path('run') / path
381
388
 
382
389
 
383
- def load_workflow_from_template(template_path: str,
384
- mapping: dict[str, str],
385
- base_path: str | Path,
386
- target_path: str | None = None,
387
- package='workflows',
388
- mtime: float = 0) -> WorkflowType:
390
+ def load_workflow_from_template(
391
+ template_path: str,
392
+ mapping: dict[str, str],
393
+ base_path: str | Path,
394
+ target_path: str | None = None,
395
+ package='workflows',
396
+ mtime: float = 0,
397
+ veryfy_source_code: bool = True) -> WorkflowType:
389
398
  base_path = Path(base_path)
390
399
  path = Path(template_path)
391
400
 
@@ -413,7 +422,8 @@ def load_workflow_from_template(template_path: str,
413
422
  f"`{file}` already exists and is different from the new one generated from template `{template_path}`"
414
423
  )
415
424
 
416
- module = load_workflow_from_file(str(path), base_path, package)
425
+ module = load_workflow_from_file(str(path), base_path, package,
426
+ veryfy_source_code)
417
427
  module.__mtime__ = max(mtime, module.__mtime__)
418
428
  if module.__source__ == content:
419
429
  module.__source__ = template, mapping, str(template_path)
@@ -425,26 +435,31 @@ def load_workflow(workflow: str | tuple[str, dict],
425
435
  base_path: str | Path,
426
436
  package='workflows',
427
437
  mtime: float = 0,
428
- inject: dict | None = None) -> WorkflowType:
438
+ inject: dict | None = None,
439
+ veryfy_source_code: bool = True) -> WorkflowType:
429
440
  if isinstance(workflow, tuple):
430
441
  if len(workflow) == 2:
431
442
  file_name, mapping = workflow
432
443
  if inject is None:
433
444
  w = load_workflow_from_template(file_name, mapping, base_path,
434
- None, package, mtime)
445
+ None, package, mtime,
446
+ veryfy_source_code)
435
447
  else:
436
448
  w = load_workflow_from_template(file_name, inject, base_path,
437
- None, package, mtime)
449
+ None, package, mtime,
450
+ veryfy_source_code)
438
451
  elif len(workflow) == 3:
439
452
  template_path, target_path, mapping = workflow
440
453
  if inject is None:
441
454
  w = load_workflow_from_template(template_path, mapping,
442
455
  base_path, target_path,
443
- package, mtime)
456
+ package, mtime,
457
+ veryfy_source_code)
444
458
  else:
445
459
  w = load_workflow_from_template(template_path, inject,
446
460
  base_path, target_path,
447
- package, mtime)
461
+ package, mtime,
462
+ veryfy_source_code)
448
463
  else:
449
464
  raise ValueError(f"Invalid workflow: {workflow}")
450
465
  w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
@@ -454,7 +469,8 @@ def load_workflow(workflow: str | tuple[str, dict],
454
469
  w = SetConfigWorkflow(key)
455
470
  w.__workflow_id__ = workflow
456
471
  else:
457
- w = load_workflow_from_file(workflow, base_path, package)
472
+ w = load_workflow_from_file(workflow, base_path, package,
473
+ veryfy_source_code)
458
474
  w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
459
475
  else:
460
476
  raise TypeError(f"Invalid workflow: {workflow}")
@@ -462,11 +478,15 @@ def load_workflow(workflow: str | tuple[str, dict],
462
478
  return w
463
479
 
464
480
 
465
- def _load_workflow_list(workflow, lst, code_path):
481
+ def _load_workflow_list(workflow, lst, code_path, veryfy_source_code):
466
482
  ret = []
467
483
  for i, n in enumerate(lst):
468
484
  try:
469
- ret.append(load_workflow(n, code_path, mtime=workflow.__mtime__))
485
+ ret.append(
486
+ load_workflow(n,
487
+ code_path,
488
+ mtime=workflow.__mtime__,
489
+ veryfy_source_code=veryfy_source_code))
470
490
  except TemplateKeyError as e:
471
491
  msg, *_ = e.args
472
492
  e.args = (
@@ -475,16 +495,18 @@ def _load_workflow_list(workflow, lst, code_path):
475
495
  return ret
476
496
 
477
497
 
478
- def get_dependents(workflow: WorkflowType,
479
- code_path: str | Path) -> list[WorkflowType]:
498
+ def get_dependents(workflow: WorkflowType, code_path: str | Path,
499
+ veryfy_source_code: bool) -> list[WorkflowType]:
480
500
  if callable(getattr(workflow, 'depends', None)):
481
501
  if not can_call_without_args(workflow.depends):
482
502
  raise AttributeError(
483
503
  f'Workflow {workflow.__workflow_id__} "depends" function should not have any parameters'
484
504
  )
485
- return _load_workflow_list(workflow, workflow.depends(), code_path)
505
+ return _load_workflow_list(workflow, workflow.depends(), code_path,
506
+ veryfy_source_code)
486
507
  elif isinstance(getattr(workflow, 'depends', None), (list, tuple)):
487
- return _load_workflow_list(workflow, workflow.depends, code_path)
508
+ return _load_workflow_list(workflow, workflow.depends, code_path,
509
+ veryfy_source_code)
488
510
  elif getattr(workflow, 'depends', None) is None:
489
511
  return []
490
512
  else:
@@ -493,16 +515,18 @@ def get_dependents(workflow: WorkflowType,
493
515
  )
494
516
 
495
517
 
496
- def get_entries(workflow: WorkflowType,
497
- code_path: str | Path) -> list[WorkflowType]:
518
+ def get_entries(workflow: WorkflowType, code_path: str | Path,
519
+ veryfy_source_code: bool) -> list[WorkflowType]:
498
520
  if callable(getattr(workflow, 'entries', None)):
499
521
  if not can_call_without_args(workflow.entries):
500
522
  raise AttributeError(
501
523
  f'Workflow {workflow.__workflow_id__} "entries" function should not have any parameters'
502
524
  )
503
- return _load_workflow_list(workflow, workflow.entries(), code_path)
525
+ return _load_workflow_list(workflow, workflow.entries(), code_path,
526
+ veryfy_source_code)
504
527
  elif isinstance(getattr(workflow, 'entries', None), (list, tuple)):
505
- return _load_workflow_list(workflow, workflow.entries, code_path)
528
+ return _load_workflow_list(workflow, workflow.entries, code_path,
529
+ veryfy_source_code)
506
530
  elif getattr(workflow, 'entries', None) is None:
507
531
  return []
508
532
  else:
@@ -511,19 +535,28 @@ def get_entries(workflow: WorkflowType,
511
535
  )
512
536
 
513
537
 
514
- def make_graph(workflow: WorkflowType, graph: dict, code_path: str | Path):
538
+ def make_graph(workflow: WorkflowType,
539
+ graph: dict,
540
+ code_path: str | Path,
541
+ veryfy_source_code: bool = True):
515
542
  if workflow.__workflow_id__ in graph:
516
543
  return graph
517
544
  graph[workflow.__workflow_id__] = []
518
545
 
519
546
  if hasattr(workflow, 'entries'):
520
- for w in get_entries(workflow, code_path):
547
+ for w in get_entries(workflow, code_path, veryfy_source_code):
521
548
  graph[workflow.__workflow_id__].append(w.__workflow_id__)
522
- make_graph(w, graph=graph, code_path=code_path)
549
+ make_graph(w,
550
+ graph=graph,
551
+ code_path=code_path,
552
+ veryfy_source_code=veryfy_source_code)
523
553
  elif hasattr(workflow, 'depends'):
524
- for w in get_dependents(workflow, code_path):
554
+ for w in get_dependents(workflow, code_path, veryfy_source_code):
525
555
  graph[workflow.__workflow_id__].append(w.__workflow_id__)
526
- make_graph(w, graph=graph, code_path=code_path)
556
+ make_graph(w,
557
+ graph=graph,
558
+ code_path=code_path,
559
+ veryfy_source_code=veryfy_source_code)
527
560
  if graph[workflow.__workflow_id__] == []:
528
561
  del graph[workflow.__workflow_id__]
529
562
 
@@ -71,7 +71,7 @@ def veryfy_analyzed_report(report: Report, script: str, method: str):
71
71
 
72
72
 
73
73
  def check_state(workflow: WorkflowType, code_path: str | Path,
74
- state_path: str | Path) -> bool:
74
+ state_path: str | Path, veryfy_source_code: bool) -> bool:
75
75
  """
76
76
  check state should report a pass if and only if the following are satisfied:
77
77
 
@@ -110,15 +110,15 @@ def check_state(workflow: WorkflowType, code_path: str | Path,
110
110
  logger.debug(
111
111
  f'check_state failed: "{workflow.__workflow_id__}" has bad data')
112
112
  return False
113
- for n in get_dependents(workflow, code_path):
113
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
114
114
  r = find_report(n.__workflow_id__, state_path)
115
115
  if r is None or r.checked_time > report.checked_time:
116
116
  logger.debug(
117
117
  f'check_state failed: "{workflow.__workflow_id__}" has outdated dependencies'
118
118
  )
119
119
  return False
120
- for n in get_dependents(workflow, code_path):
121
- if not check_state(n, code_path, state_path):
120
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
121
+ if not check_state(n, code_path, state_path, veryfy_source_code):
122
122
  logger.debug(
123
123
  f'check_state failed: "{workflow.__workflow_id__}" has bad dependencies'
124
124
  )
@@ -126,7 +126,7 @@ def check_state(workflow: WorkflowType, code_path: str | Path,
126
126
  return True
127
127
 
128
128
 
129
- @logger.catch()
129
+ @logger.catch(reraise=False)
130
130
  async def call_plot(node: WorkflowType, report: Report, check=False):
131
131
  if hasattr(node, 'plot') and callable(node.plot):
132
132
  if inspect.iscoroutinefunction(node.plot):
@@ -361,7 +361,8 @@ async def calibrate(workflow: WorkflowType, state_path: str | Path, plot: bool,
361
361
 
362
362
 
363
363
  async def diagnose(workflow: WorkflowType, code_path: str | Path,
364
- state_path: str | Path, plot: bool, session_id: str):
364
+ state_path: str | Path, plot: bool, session_id: str,
365
+ fail_fast: bool, veryfy_source_code: bool):
365
366
  '''
366
367
  Returns: True if node or dependent recalibrated.
367
368
  '''
@@ -379,10 +380,20 @@ async def diagnose(workflow: WorkflowType, code_path: str | Path,
379
380
  if report.bad_data:
380
381
  logger.debug(
381
382
  f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
382
- recalibrated = [
383
- await diagnose(n, code_path, state_path, plot, session_id)
384
- for n in get_dependents(workflow, code_path)
385
- ]
383
+ recalibrated = []
384
+ exceptions = []
385
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
386
+ try:
387
+ flag = await diagnose(n, code_path, state_path, plot,
388
+ session_id, fail_fast,
389
+ veryfy_source_code)
390
+ except Exception as e:
391
+ if fail_fast:
392
+ raise e
393
+ exceptions.append(e)
394
+ recalibrated.append(flag)
395
+ if any(exceptions):
396
+ raise exceptions[0]
386
397
  if not any(recalibrated):
387
398
  if report.bad_data:
388
399
  raise CalibrationFailedError(
@@ -427,28 +438,41 @@ async def maintain(workflow: WorkflowType,
427
438
  session_id: str | None = None,
428
439
  run: bool = False,
429
440
  plot: bool = False,
430
- freeze: bool = False):
441
+ freeze: bool = False,
442
+ fail_fast: bool = False,
443
+ veryfy_source_code: bool = True):
431
444
  if session_id is None:
432
445
  session_id = uuid.uuid4().hex
433
446
  logger.debug(f'run "{workflow.__workflow_id__}"'
434
447
  if run else f'maintain "{workflow.__workflow_id__}"')
435
448
  # recursive maintain
436
- for n in get_dependents(workflow, code_path):
449
+ exceptions = []
450
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
437
451
  logger.debug(
438
452
  f'maintain "{n.__workflow_id__}" because it is depended by "{workflow.__workflow_id__}"'
439
453
  )
440
- await maintain(n,
441
- code_path,
442
- state_path,
443
- session_id,
444
- run=False,
445
- plot=plot,
446
- freeze=freeze)
454
+ try:
455
+ await maintain(n,
456
+ code_path,
457
+ state_path,
458
+ session_id,
459
+ run=False,
460
+ plot=plot,
461
+ freeze=freeze,
462
+ fail_fast=fail_fast,
463
+ veryfy_source_code=veryfy_source_code)
464
+ except Exception as e:
465
+ if fail_fast:
466
+ raise e
467
+ exceptions.append(e)
447
468
  else:
448
469
  logger.debug(
449
470
  f'"{workflow.__workflow_id__}": All dependents maintained')
471
+ if any(exceptions):
472
+ raise exceptions[0]
450
473
  # check_state
451
- if check_state(workflow, code_path, state_path) and not run:
474
+ if check_state(workflow, code_path, state_path,
475
+ veryfy_source_code) and not run:
452
476
  logger.debug(
453
477
  f'"{workflow.__workflow_id__}": In spec, no need to maintain')
454
478
  return
@@ -462,14 +486,23 @@ async def maintain(workflow: WorkflowType,
462
486
  elif report.bad_data:
463
487
  logger.debug(
464
488
  f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
465
- for n in get_dependents(workflow, code_path):
489
+ exceptions = []
490
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
466
491
  logger.debug(
467
492
  f'diagnose "{n.__workflow_id__}" because of "{workflow.__workflow_id__}" bad data'
468
493
  )
469
- await diagnose(n, code_path, state_path, plot, session_id)
494
+ try:
495
+ await diagnose(n, code_path, state_path, plot, session_id,
496
+ fail_fast, veryfy_source_code)
497
+ except Exception as e:
498
+ if fail_fast:
499
+ raise e
500
+ exceptions.append(e)
470
501
  else:
471
502
  logger.debug(
472
503
  f'"{workflow.__workflow_id__}": All dependents diagnosed')
504
+ if any(exceptions):
505
+ raise exceptions[0]
473
506
  # calibrate
474
507
  logger.debug(f'recalibrate "{workflow.__workflow_id__}"')
475
508
  report = await calibrate(workflow, state_path, plot, session_id)
@@ -33,11 +33,11 @@ def dependent_tree(node: str, code_path: str | Path) -> dict[str, list[str]]:
33
33
 
34
34
 
35
35
  def workflow_template(workflow: str, deps: list[str]) -> str:
36
- return f"""def VAR(s): pass # 没有实际作用,只是用来抑制编辑器的警告。
37
-
36
+ return f"""
38
37
  import numpy as np
39
38
  from loguru import logger
40
39
 
40
+ from qulab import VAR
41
41
  from qulab.typing import Report
42
42
 
43
43
 
@@ -48,7 +48,7 @@ def depends():
48
48
  return {deps!r}
49
49
 
50
50
 
51
- def calibrate():
51
+ async def calibrate():
52
52
  logger.info(f"running {workflow} ...")
53
53
 
54
54
  # calibrate 是一个完整的校准实验,如power Rabi,Ramsey等。
@@ -64,7 +64,7 @@ def calibrate():
64
64
  return x, y
65
65
 
66
66
 
67
- def analyze(report: Report, history: Report | None = None) -> Report:
67
+ async def analyze(report: Report, history: Report | None = None) -> Report:
68
68
  \"\"\"
69
69
  分析校准结果。
70
70
 
@@ -95,7 +95,7 @@ def analyze(report: Report, history: Report | None = None) -> Report:
95
95
  return report
96
96
 
97
97
 
98
- def check():
98
+ async def check():
99
99
  logger.info(f"checking {workflow} ...")
100
100
 
101
101
  # check 是一个快速检查实验,用于检查校准是否过时。
@@ -112,7 +112,7 @@ def check():
112
112
  return x, y
113
113
 
114
114
 
115
- def check_analyze(report: Report, history: Report | None = None) -> Report:
115
+ async def check_analyze(report: Report, history: Report | None = None) -> Report:
116
116
  \"\"\"
117
117
  分析检查结果。
118
118
 
@@ -136,7 +136,7 @@ def check_analyze(report: Report, history: Report | None = None) -> Report:
136
136
  return report
137
137
 
138
138
 
139
- def oracle(report: Report,
139
+ async def oracle(report: Report,
140
140
  history: Report | None = None,
141
141
  system_state: dict[str:str] | None = None) -> Report:
142
142
  \"\"\"
@@ -1,3 +1,4 @@
1
+ import copy
1
2
  import itertools
2
3
  import logging
3
4
  import re
@@ -127,7 +128,8 @@ class BaseDevice(metaclass=DeviceMeta):
127
128
  __log__ = None
128
129
 
129
130
  def __init__(self, address: str = None, **options):
130
- self._status = {}
131
+ self._state = {}
132
+ self._status = "NOT CONNECTED"
131
133
  self.address = address
132
134
  self.options = options
133
135
 
@@ -144,28 +146,40 @@ class BaseDevice(metaclass=DeviceMeta):
144
146
  f"{self.__class__.__module__}.{self.__class__.__name__}")
145
147
  return self.__log__
146
148
 
149
+ @property
150
+ def state(self):
151
+ return copy.deepcopy(self._state)
152
+
153
+ def get_state(self, key: str, default=None) -> Any:
154
+ return self._state.get(key, default)
155
+
156
+ @property
157
+ def status(self):
158
+ return self._status
159
+
147
160
  def open(self) -> None:
148
- pass
161
+ self._status = "CONNECTED"
149
162
 
150
163
  def close(self) -> None:
151
- pass
164
+ self._status = "NOT CONNECTED"
165
+ self._state.clear()
152
166
 
153
167
  def reset(self) -> None:
154
- self._status.clear()
168
+ self._state.clear()
155
169
 
156
170
  def get(self, key: str, default: Any = None) -> Any:
157
171
  self.log.info(f'Get {key!r}')
158
172
  if key in self.__get_actions__:
159
173
  result = self.__get_actions__[key](self)
160
- self._status[key] = result
174
+ self._state[key] = result
161
175
  return result
162
176
  else:
163
- return self._status.get(key, default)
177
+ return self._state.get(key, default)
164
178
 
165
179
  def set(self, key: str, value: Any = None) -> None:
166
180
  self.log.info(f'Set {key!r} = {value!r}')
167
181
  self.__set_actions__[key](self, value)
168
- self._status[key] = value
182
+ self._state[key] = value
169
183
 
170
184
  def post(self, key: str, value: Any = None) -> Any:
171
185
  self.log.info(f'Post {key!r} = {value!r}')
@@ -174,7 +188,7 @@ class BaseDevice(metaclass=DeviceMeta):
174
188
  def delete(self, key: str) -> None:
175
189
  self.log.info(f'Delete {key!r}')
176
190
  self.__delete_actions__[key](self)
177
- del self._status[key]
191
+ del self._state[key]
178
192
 
179
193
  def __repr__(self) -> str:
180
194
  return f'{self.__class__.__name__}({self.address!r})'
@@ -193,8 +207,10 @@ class VisaDevice(BaseDevice):
193
207
  rm = pyvisa.ResourceManager()
194
208
  self.resource = rm.open_resource(self.address, **kwds)
195
209
  self.errors = []
210
+ super().open()
196
211
 
197
212
  def close(self) -> None:
213
+ super().close()
198
214
  self.resource.close()
199
215
 
200
216
  def reset(self) -> None:
@@ -35,7 +35,7 @@ class Device(BaseDevice):
35
35
 
36
36
  # @get('${channel}.Vpp', channel=exclude(['M1', 'M2']))
37
37
  # def get_voltage(self, channel: str, default=0.0) -> float:
38
- # return self._status.get(f'{channel}.Vpp', default)
38
+ # return self._state.get(f'{channel}.Vpp', default)
39
39
 
40
40
  @set('${channel}.Offset', channel=exclude(['M1', 'M2']))
41
41
  def set_offset(
@@ -47,7 +47,7 @@ class Device(BaseDevice):
47
47
 
48
48
  # @get('${channel}.Offset', channel=exclude(['M1', 'M2']))
49
49
  # def get_offset(self, channel: str, default=0.0) -> float:
50
- # return self._status.get(f'{channel}.Offset', default)
50
+ # return self._state.get(f'{channel}.Offset', default)
51
51
 
52
52
  @set('${channel}.Waveform', channel=exclude(['M1', 'M2']))
53
53
  def set_waveform(self, value, channel: str) -> None:
@@ -55,7 +55,7 @@ class Device(BaseDevice):
55
55
 
56
56
  # @get('${channel}.Waveform', channel=exclude(['M1', 'M2']))
57
57
  # def get_waveform(self, channel: str, default=None) -> str:
58
- # return self._status.get(f'{channel}.Waveform', default)
58
+ # return self._state.get(f'{channel}.Waveform', default)
59
59
 
60
60
  @set('${channel}.Size', channel=['M1', 'M2'])
61
61
  def set_size(self, value, channel: str) -> None:
@@ -63,6 +63,6 @@ class Device(BaseDevice):
63
63
 
64
64
  @get('${channel}.Trace', channel=['M1', 'M2'])
65
65
  def get_random_data(self, channel: str) -> np.ndarray:
66
- size = self._status.get(f'{channel}.Size', 1024)
67
- shots = self._status.get(f'{channel}.Shots', 128)
66
+ size = self._state.get(f'{channel}.Size', 1024)
67
+ shots = self._state.get(f'{channel}.Shots', 128)
68
68
  return np.random.randn(shots, size)
@@ -0,0 +1 @@
1
+ __version__ = "2.10.3"
@@ -1 +0,0 @@
1
- __version__ = "2.10.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes