QuLab 2.4.10__tar.gz → 2.4.11__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 (108) hide show
  1. {qulab-2.4.10 → qulab-2.4.11}/PKG-INFO +1 -1
  2. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/PKG-INFO +1 -1
  3. {qulab-2.4.10 → qulab-2.4.11}/qulab/__main__.py +3 -1
  4. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/__main__.py +74 -6
  5. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/load.py +72 -10
  6. qulab-2.4.11/qulab/executor/schedule.py +304 -0
  7. qulab-2.4.11/qulab/version.py +1 -0
  8. qulab-2.4.10/qulab/executor/schedule.py +0 -264
  9. qulab-2.4.10/qulab/version.py +0 -1
  10. {qulab-2.4.10 → qulab-2.4.11}/LICENSE +0 -0
  11. {qulab-2.4.10 → qulab-2.4.11}/MANIFEST.in +0 -0
  12. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/SOURCES.txt +0 -0
  13. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/dependency_links.txt +0 -0
  14. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/entry_points.txt +0 -0
  15. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/requires.txt +0 -0
  16. {qulab-2.4.10 → qulab-2.4.11}/QuLab.egg-info/top_level.txt +0 -0
  17. {qulab-2.4.10 → qulab-2.4.11}/README.md +0 -0
  18. {qulab-2.4.10 → qulab-2.4.11}/pyproject.toml +0 -0
  19. {qulab-2.4.10 → qulab-2.4.11}/qulab/__init__.py +0 -0
  20. {qulab-2.4.10 → qulab-2.4.11}/qulab/dicttree.py +0 -0
  21. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/__init__.py +0 -0
  22. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/storage.py +0 -0
  23. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/transform.py +0 -0
  24. {qulab-2.4.10 → qulab-2.4.11}/qulab/executor/utils.py +0 -0
  25. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/__init__.py +0 -0
  26. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/__main__.py +0 -0
  27. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/config.py +0 -0
  28. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/dataset.py +0 -0
  29. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/event_queue.py +0 -0
  30. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/mainwindow.py +0 -0
  31. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/monitor.py +0 -0
  32. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/ploter.py +0 -0
  33. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/qt_compat.py +0 -0
  34. {qulab-2.4.10 → qulab-2.4.11}/qulab/monitor/toolbar.py +0 -0
  35. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/__init__.py +0 -0
  36. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/curd.py +0 -0
  37. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/expression.py +0 -0
  38. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/models.py +0 -0
  39. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/optimize.py +0 -0
  40. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/query.py +0 -0
  41. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/record.py +0 -0
  42. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/scan.py +0 -0
  43. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/server.py +0 -0
  44. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/space.py +0 -0
  45. {qulab-2.4.10 → qulab-2.4.11}/qulab/scan/utils.py +0 -0
  46. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/__init__.py +0 -0
  47. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/__main__.py +0 -0
  48. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/backend/__init__.py +0 -0
  49. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/backend/redis.py +0 -0
  50. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/base_dataset.py +0 -0
  51. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/chunk.py +0 -0
  52. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/dataset.py +0 -0
  53. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/file.py +0 -0
  54. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/__init__.py +0 -0
  55. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/base.py +0 -0
  56. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/config.py +0 -0
  57. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/file.py +0 -0
  58. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/ipy.py +0 -0
  59. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/models.py +0 -0
  60. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/record.py +0 -0
  61. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/report.py +0 -0
  62. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/models/tag.py +0 -0
  63. {qulab-2.4.10 → qulab-2.4.11}/qulab/storage/storage.py +0 -0
  64. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/__init__.py +0 -0
  65. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/chat.py +0 -0
  66. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/device/__init__.py +0 -0
  67. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/device/basedevice.py +0 -0
  68. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/device/loader.py +0 -0
  69. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/device/utils.py +0 -0
  70. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/drivers/FakeInstrument.py +0 -0
  71. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/drivers/__init__.py +0 -0
  72. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/ipy_events.py +0 -0
  73. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/__init__.py +0 -0
  74. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/bencoder.py +0 -0
  75. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/cli.py +0 -0
  76. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/dhcp.py +0 -0
  77. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/dhcpd.py +0 -0
  78. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/kad.py +0 -0
  79. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/kcp.py +0 -0
  80. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/net/nginx.py +0 -0
  81. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/progress.py +0 -0
  82. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/__init__.py +0 -0
  83. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/client.py +0 -0
  84. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/exceptions.py +0 -0
  85. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/msgpack.py +0 -0
  86. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/msgpack.pyi +0 -0
  87. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/router.py +0 -0
  88. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/rpc.py +0 -0
  89. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/serialize.py +0 -0
  90. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/server.py +0 -0
  91. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/socket.py +0 -0
  92. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/utils.py +0 -0
  93. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/worker.py +0 -0
  94. {qulab-2.4.10 → qulab-2.4.11}/qulab/sys/rpc/zmq_socket.py +0 -0
  95. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/__init__.py +0 -0
  96. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/__main__.py +0 -0
  97. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/_autoplot.py +0 -0
  98. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/plot_circ.py +0 -0
  99. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/plot_layout.py +0 -0
  100. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/plot_seq.py +0 -0
  101. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/qdat.py +0 -0
  102. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/rot3d.py +0 -0
  103. {qulab-2.4.10 → qulab-2.4.11}/qulab/visualization/widgets.py +0 -0
  104. {qulab-2.4.10 → qulab-2.4.11}/setup.cfg +0 -0
  105. {qulab-2.4.10 → qulab-2.4.11}/setup.py +0 -0
  106. {qulab-2.4.10 → qulab-2.4.11}/src/qulab.h +0 -0
  107. {qulab-2.4.10 → qulab-2.4.11}/tests/test_kad.py +0 -0
  108. {qulab-2.4.10 → qulab-2.4.11}/tests/test_scan.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: QuLab
3
- Version: 2.4.10
3
+ Version: 2.4.11
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.2
2
2
  Name: QuLab
3
- Version: 2.4.10
3
+ Version: 2.4.11
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
  import click
2
2
 
3
- from .executor.__main__ import create, maintain, run
3
+ from .executor.__main__ import create, get, maintain, run, set
4
4
  from .monitor.__main__ import main as monitor
5
5
  from .scan.server import server
6
6
  from .sys.net.cli import dht
@@ -25,6 +25,8 @@ cli.add_command(server)
25
25
  cli.add_command(maintain)
26
26
  cli.add_command(run)
27
27
  cli.add_command(create)
28
+ cli.add_command(set)
29
+ cli.add_command(get)
28
30
 
29
31
  if __name__ == '__main__':
30
32
  cli()
@@ -1,25 +1,50 @@
1
1
  import functools
2
2
  import importlib
3
- import sys, os
3
+ import os
4
+ import sys
4
5
  from pathlib import Path
5
6
 
6
7
  import click
7
8
  from loguru import logger
8
9
 
9
- from .load import find_unreferenced_workflows
10
+ from .load import find_unreferenced_workflows, load_workflow
10
11
  from .schedule import maintain as maintain_workflow
11
12
  from .schedule import run as run_workflow
12
13
  from .transform import set_config_api
13
14
  from .utils import workflow_template
14
15
 
15
16
 
17
+ def load_config():
18
+ import yaml
19
+
20
+ config_paths = [
21
+ Path.home() / ".myapp/config.yaml", # 用户主目录
22
+ Path("config.yaml") # 当前目录
23
+ ]
24
+ for path in config_paths:
25
+ if path.exists():
26
+ with open(path) as f:
27
+ return yaml.safe_load(f)
28
+ return {"defaults": {"log": "default.log", "debug": False}}
29
+
30
+
31
+ def get_config_value(option_name):
32
+ # 1. 尝试从环境变量读取
33
+ env_value = os.environ.get(f"MYAPP_{option_name.upper()}")
34
+ if env_value:
35
+ return env_value
36
+
37
+ # 2. 尝试从配置文件读取
38
+ config = load_config()
39
+ return config["defaults"].get(option_name)
40
+
41
+
16
42
  def log_options(func):
17
43
 
18
44
  @click.option("--debug", is_flag=True, help="Enable debug mode.")
19
45
  @click.option("--log", type=str, help="Log file path.")
20
46
  @functools.wraps(func) # 保持函数元信息
21
47
  def wrapper(*args, log=None, debug=False, **kwargs):
22
- print(f"{func} {log=}, {debug=}")
23
48
  if log is None and not debug:
24
49
  logger.remove()
25
50
  logger.add(sys.stderr, level='INFO')
@@ -67,6 +92,39 @@ def create(workflow, code):
67
92
  click.echo(f'{workflow} created')
68
93
 
69
94
 
95
+ @click.command()
96
+ @click.argument('key')
97
+ @click.argument('value', type=str)
98
+ @click.option('--api', '-a', default=None, help='The modlule name of the api.')
99
+ def set(key, value, api):
100
+ """
101
+ Set a config.
102
+ """
103
+ from . import transform
104
+ if api is not None:
105
+ api = importlib.import_module(api)
106
+ set_config_api(api.query_config, api.update_config)
107
+ try:
108
+ value = eval(value)
109
+ except:
110
+ pass
111
+ transform.update_config({key: value})
112
+
113
+
114
+ @click.command()
115
+ @click.argument('key')
116
+ @click.option('--api', '-a', default=None, help='The modlule name of the api.')
117
+ def get(key, api):
118
+ """
119
+ Get a config.
120
+ """
121
+ from . import transform
122
+ if api is not None:
123
+ api = importlib.import_module(api)
124
+ set_config_api(api.query_config, api.update_config)
125
+ click.echo(transform.query_config(key))
126
+
127
+
70
128
  @click.command()
71
129
  @click.argument('workflow')
72
130
  @click.option('--code', '-c', default=None, help='The path of the code.')
@@ -94,9 +152,13 @@ def run(workflow, code, data, api, plot, no_dependents):
94
152
  data = Path(os.path.expanduser(data))
95
153
 
96
154
  if no_dependents:
97
- run_workflow(workflow, code, data, plot=plot)
155
+ run_workflow(load_workflow(workflow, code), code, data, plot=plot)
98
156
  else:
99
- maintain_workflow(workflow, code, data, run=True, plot=plot)
157
+ maintain_workflow(load_workflow(workflow, code),
158
+ code,
159
+ data,
160
+ run=True,
161
+ plot=plot)
100
162
 
101
163
 
102
164
  @click.command()
@@ -121,12 +183,18 @@ def maintain(workflow, code, data, api, plot):
121
183
  code = Path(os.path.expanduser(code))
122
184
  data = Path(os.path.expanduser(data))
123
185
 
124
- maintain_workflow(workflow, code, data, run=False, plot=plot)
186
+ maintain_workflow(load_workflow(workflow, code),
187
+ code,
188
+ data,
189
+ run=False,
190
+ plot=plot)
125
191
 
126
192
 
127
193
  cli.add_command(maintain)
128
194
  cli.add_command(run)
129
195
  cli.add_command(create)
196
+ cli.add_command(set)
197
+ cli.add_command(get)
130
198
 
131
199
  if __name__ == '__main__':
132
200
  cli()
@@ -1,10 +1,14 @@
1
+ import hashlib
1
2
  import inspect
3
+ import pickle
4
+ import re
5
+ import string
2
6
  import warnings
3
7
  from importlib.util import module_from_spec, spec_from_file_location
4
8
  from pathlib import Path
5
9
  from types import ModuleType
6
10
 
7
- import loguru
11
+ from loguru import logger
8
12
 
9
13
  from .storage import Result
10
14
 
@@ -124,12 +128,15 @@ def find_unreferenced_workflows(path: str) -> list[str]:
124
128
  for file_path in root.rglob("*.py"):
125
129
  if file_path.name == "__init__.py":
126
130
  continue
131
+ if file_path.name.endswith("_template.py") or re.match(
132
+ r'.*_tmp_[0-9a-fA-F]{8}.py', file_path.name):
133
+ continue
127
134
  try:
128
135
  rel_path = file_path.relative_to(root)
129
136
  except ValueError:
130
137
  continue
131
138
 
132
- module = load_workflow(str(rel_path), root)
139
+ module = load_workflow_from_module(str(rel_path), root)
133
140
 
134
141
  if is_workflow(module):
135
142
  rel_str = str(rel_path)
@@ -140,7 +147,7 @@ def find_unreferenced_workflows(path: str) -> list[str]:
140
147
 
141
148
  # Check dependencies for each workflow module
142
149
  for rel_str in workflows:
143
- module = load_workflow(rel_str, root)
150
+ module = load_workflow_from_module(rel_str, root)
144
151
 
145
152
  depends_func = getattr(module, "depends", None)
146
153
  if depends_func and callable(depends_func):
@@ -156,11 +163,9 @@ def find_unreferenced_workflows(path: str) -> list[str]:
156
163
  continue
157
164
 
158
165
  if not isinstance(depends_list, list) or not all(
159
- isinstance(item, str) for item in depends_list
160
- ):
166
+ isinstance(item, str) for item in depends_list):
161
167
  warnings.warn(
162
- f"depends() in {rel_str} did not return a list of strings"
163
- )
168
+ f"depends() in {rel_str} did not return a list of strings")
164
169
  continue
165
170
 
166
171
  for dep in depends_list:
@@ -178,9 +183,9 @@ def find_unreferenced_workflows(path: str) -> list[str]:
178
183
  return unreferenced
179
184
 
180
185
 
181
- def load_workflow(file_name: str,
182
- base_path: str | Path,
183
- package='workflows') -> WorkflowType:
186
+ def load_workflow_from_module(file_name: str,
187
+ base_path: str | Path,
188
+ package='workflows') -> WorkflowType:
184
189
  if file_name.startswith('cfg:'):
185
190
  return SetConfigWorkflow(file_name[4:])
186
191
  base_path = Path(base_path)
@@ -200,3 +205,60 @@ def load_workflow(file_name: str,
200
205
  verify_check_method(module)
201
206
 
202
207
  return module
208
+
209
+
210
+ def load_workflow_from_template(file_name: str,
211
+ mappping: dict[str, str],
212
+ base_path: str | Path,
213
+ subtitle: str | None = None,
214
+ package='workflows') -> WorkflowType:
215
+ base_path = Path(base_path)
216
+ path = Path(file_name)
217
+
218
+ with open(base_path / path) as f:
219
+ content = f.read()
220
+ template = string.Template(content)
221
+ content = template.substitute(mappping)
222
+
223
+ hash_str = hashlib.md5(pickle.dumps(mappping)).hexdigest()[:8]
224
+ if subtitle is None:
225
+ path = path.parent / path.stem.replace('_template',
226
+ f'_tmp{hash_str}.py')
227
+ else:
228
+ path = path.parent / path.stem.replace('_template', f'_{subtitle}.py')
229
+
230
+ with open(base_path / path, 'w') as f:
231
+ f.write(content)
232
+
233
+ module = load_workflow_from_module(str(path), base_path, package)
234
+
235
+ return module
236
+
237
+
238
+ def load_workflow(workflow: str | tuple[str, dict],
239
+ base_path: str | Path,
240
+ package='workflows') -> WorkflowType:
241
+ if isinstance(workflow, tuple):
242
+ if len(workflow) == 2:
243
+ file_name, mapping = workflow
244
+ w = load_workflow_from_template(file_name, mapping, base_path,
245
+ None, package)
246
+ elif len(workflow) == 3:
247
+ file_name, subtitle, mapping = workflow
248
+ w = load_workflow_from_template(file_name, mapping, base_path,
249
+ subtitle, package)
250
+ else:
251
+ raise ValueError(f"Invalid workflow: {workflow}")
252
+ w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
253
+ elif isinstance(workflow, str):
254
+ if workflow.startswith('cfg:'):
255
+ key = workflow[4:]
256
+ w = SetConfigWorkflow(key)
257
+ w.__workflow_id__ = workflow
258
+ else:
259
+ w = load_workflow_from_module(workflow, base_path, package)
260
+ w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
261
+ else:
262
+ raise TypeError(f"Invalid workflow: {workflow}")
263
+
264
+ return w
@@ -0,0 +1,304 @@
1
+ import functools
2
+ import uuid
3
+ from datetime import datetime, timedelta
4
+ from pathlib import Path
5
+
6
+ from loguru import logger
7
+
8
+ from . import transform
9
+ from .load import WorkflowType, load_workflow
10
+ from .storage import (Result, find_result, get_head, renew_result,
11
+ revoke_result, save_result)
12
+
13
+
14
+ class CalibrationFailedError(Exception):
15
+ pass
16
+
17
+
18
+ def check_state(workflow: WorkflowType, code_path: str | Path,
19
+ state_path: str | Path) -> bool:
20
+ """
21
+ check state should report a pass if and only if the following are satisfied:
22
+
23
+ 1. The cal has had check data or calibrate pass within the timeout period.
24
+ 2. The cal has not failed calibrate without resolution.
25
+ 3. No dependencies have been recalibrated since the last time check data or calibrate was run on this cal.
26
+ 4. All dependencies pass check state.
27
+ """
28
+ logger.debug(f'check_state: "{workflow}"')
29
+ result = find_result(workflow.__workflow_id__, state_path)
30
+ if not result:
31
+ logger.debug(
32
+ f'check_state failed: No history found for "{workflow.__workflow_id__}"'
33
+ )
34
+ return False
35
+ if hasattr(workflow, 'check_state') and callable(workflow.check_state):
36
+ logger.debug(
37
+ f'check_state: "{workflow.__workflow_id__}" has custom check_state method'
38
+ )
39
+ return workflow.check_state(result)
40
+ if workflow.__timeout__ is not None and datetime.now(
41
+ ) > result.checked_time + timedelta(seconds=workflow.__timeout__):
42
+ logger.debug(
43
+ f'check_state failed: "{workflow.__workflow_id__}" has expired')
44
+ return False
45
+ if not result.in_spec:
46
+ logger.debug(
47
+ f'check_state failed: "{workflow.__workflow_id__}" is out of spec')
48
+ return False
49
+ if result.bad_data:
50
+ logger.debug(
51
+ f'check_state failed: "{workflow.__workflow_id__}" has bad data')
52
+ return False
53
+ for n in get_dependents(workflow, code_path):
54
+ r = find_result(n, state_path)
55
+ if r is None or r.checked_time > result.checked_time:
56
+ logger.debug(
57
+ f'check_state failed: "{workflow.__workflow_id__}" has outdated dependencies'
58
+ )
59
+ return False
60
+ for n in get_dependents(workflow, code_path):
61
+ if not check_state(n, code_path, state_path):
62
+ logger.debug(
63
+ f'check_state failed: "{workflow.__workflow_id__}" has bad dependencies'
64
+ )
65
+ return False
66
+ return True
67
+
68
+
69
+ def call_analyzer(node, data, history, check=False, plot=False):
70
+ if check:
71
+ result = transform.params_to_result(
72
+ node.check_analyze(*data,
73
+ history=transform.result_to_params(history)))
74
+ result.fully_calibrated = False
75
+ else:
76
+ result = transform.params_to_result(
77
+ node.analyze(*data, history=transform.result_to_params(history)))
78
+ result.fully_calibrated = True
79
+ if plot:
80
+ call_plot(node, result)
81
+ result.data = data
82
+ return result
83
+
84
+
85
+ @logger.catch()
86
+ def call_plot(node, result, check=False):
87
+ if hasattr(node, 'plot') and callable(node.plot):
88
+ state, params, other = transform.result_to_params(result)
89
+ node.plot(state, params, other)
90
+
91
+
92
+ @functools.lru_cache(maxsize=128)
93
+ def check_data(workflow: WorkflowType, code_path: str | Path,
94
+ state_path: str | Path, plot: bool, session_id: str) -> Result:
95
+ """
96
+ check data answers two questions:
97
+ Is the parameter associated with this cal in spec,
98
+ and is the cal scan working as expected?
99
+ """
100
+ history = find_result(workflow.__workflow_id__, state_path)
101
+
102
+ if history is None:
103
+ logger.debug(f'No history found for "{workflow.__workflow_id__}"')
104
+ result = Result()
105
+ result.in_spec = False
106
+ result.bad_data = False
107
+ return result
108
+
109
+ if history.bad_data:
110
+ logger.debug(
111
+ f'History found for "{workflow.__workflow_id__}", but bad data')
112
+ return history
113
+ if not history.in_spec:
114
+ logger.debug(
115
+ f'History found for "{workflow.__workflow_id__}", but out of spec')
116
+ return history
117
+
118
+ logger.debug(
119
+ f'History found for "{workflow.__workflow_id__}", but has expired')
120
+
121
+ if hasattr(workflow, 'check') and callable(workflow.check) and hasattr(
122
+ workflow, 'check_analyze') and callable(workflow.check_analyze):
123
+ logger.debug(f'Checking "{workflow}" with "check" method ...')
124
+ data = workflow.check()
125
+ result = Result()
126
+ result.data = data
127
+ save_result(workflow.__workflow_id__, result, state_path)
128
+
129
+ logger.debug(f'Checked "{workflow}" !')
130
+ result = call_analyzer(workflow, data, history, check=True, plot=plot)
131
+ if result.in_spec:
132
+ logger.debug(
133
+ f'"{workflow.__workflow_id__}": checked in spec, renewing result'
134
+ )
135
+ renew_result(workflow.__workflow_id__, state_path)
136
+ else:
137
+ logger.debug(
138
+ f'"{workflow.__workflow_id__}": checked out of spec, revoking result'
139
+ )
140
+ revoke_result(workflow.__workflow_id__, state_path)
141
+ else:
142
+ logger.debug(
143
+ f'Checking "{workflow.__workflow_id__}" with "calibrate" method ...'
144
+ )
145
+ data = workflow.calibrate()
146
+ result = Result()
147
+ result.data = data
148
+ save_result(workflow.__workflow_id__, result, state_path)
149
+
150
+ logger.debug(f'Calibrated "{workflow}" !')
151
+ result = call_analyzer(workflow, data, history, check=False, plot=plot)
152
+ save_result(workflow.__workflow_id__, result, state_path,
153
+ get_head(workflow.__workflow_id__, state_path))
154
+
155
+ return result
156
+
157
+
158
+ @functools.lru_cache(maxsize=128)
159
+ def calibrate(workflow: WorkflowType, code_path: str | Path,
160
+ state_path: str | Path, plot: bool, session_id: str) -> Result:
161
+ history = find_result(workflow, state_path)
162
+
163
+ logger.debug(f'Calibrating "{workflow.__workflow_id__}" ...')
164
+ data = workflow.calibrate()
165
+ result = Result()
166
+ result.data = data
167
+ save_result(workflow.__workflow_id__, result, state_path)
168
+ logger.debug(f'Calibrated "{workflow.__workflow_id__}" !')
169
+ result = call_analyzer(workflow, data, history, check=False, plot=plot)
170
+ save_result(workflow.__workflow_id__, result, state_path,
171
+ get_head(workflow.__workflow_id__, state_path))
172
+ return result
173
+
174
+
175
+ def diagnose(workflow: WorkflowType, code_path: str | Path,
176
+ state_path: str | Path, plot: bool, session_id: str):
177
+ '''
178
+ Returns: True if node or dependent recalibrated.
179
+ '''
180
+ logger.debug(f'diagnose "{workflow.__workflow_id__}"')
181
+ # check_data
182
+ result = check_data(workflow, code_path, state_path, plot, session_id)
183
+ # in spec case
184
+ if result.in_spec:
185
+ logger.debug(
186
+ f'"{workflow.__workflow_id__}": Checked! In spec, no need to diagnose'
187
+ )
188
+ return False
189
+ # bad data case
190
+ recalibrated = []
191
+ if result.bad_data:
192
+ logger.debug(
193
+ f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
194
+ recalibrated = [
195
+ diagnose(n, code_path, state_path, plot, session_id)
196
+ for n in get_dependents(workflow, code_path)
197
+ ]
198
+ if not any(recalibrated):
199
+ if result.bad_data:
200
+ raise CalibrationFailedError(
201
+ f'"{workflow.__workflow_id__}": bad data but no dependents recalibrated.'
202
+ )
203
+ logger.debug(
204
+ f'"{workflow.__workflow_id__}": no dependents recalibrated.')
205
+ # calibrate
206
+ if any(recalibrated):
207
+ logger.debug(
208
+ f'recalibrate "{workflow.__workflow_id__}" because some dependents recalibrated.'
209
+ )
210
+ elif not result.in_spec and not result.bad_data:
211
+ logger.debug(
212
+ f'recalibrate "{workflow.__workflow_id__}" because out of spec.')
213
+ elif result.in_spec:
214
+ logger.error(
215
+ f'Never reach: recalibrate "{workflow.__workflow_id__}" because in spec.'
216
+ )
217
+ elif result.bad_data:
218
+ logger.error(
219
+ f'Never reach: recalibrate "{workflow.__workflow_id__}" because bad data.'
220
+ )
221
+ else:
222
+ logger.error(f'Never reach: recalibrate "{workflow.__workflow_id__}"')
223
+
224
+ result = calibrate(workflow, code_path, state_path, plot, session_id)
225
+ if result.bad_data or not result.in_spec:
226
+ raise CalibrationFailedError(
227
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
228
+ )
229
+ transform.update_parameters(result)
230
+ return True
231
+
232
+
233
+ def get_dependents(workflow: WorkflowType,
234
+ code_path: str | Path) -> list[WorkflowType]:
235
+ return [load_workflow(n, code_path) for n in workflow.depends()[0]]
236
+
237
+
238
+ @logger.catch(reraise=True)
239
+ def maintain(workflow: WorkflowType,
240
+ code_path: str | Path,
241
+ state_path: str | Path,
242
+ session_id: str | None = None,
243
+ run: bool = False,
244
+ plot: bool = False):
245
+ if session_id is None:
246
+ session_id = uuid.uuid4().hex
247
+ logger.debug(f'run "{workflow.__workflow_id__}"'
248
+ if run else f'maintain "{workflow.__workflow_id__}"')
249
+ # recursive maintain
250
+ for n in get_dependents(workflow, code_path):
251
+ logger.debug(
252
+ f'maintain "{n.__workflow_id__}" because it is depended by "{workflow.__workflow_id__}"'
253
+ )
254
+ maintain(n, code_path, state_path, session_id, run=False, plot=plot)
255
+ else:
256
+ logger.debug(
257
+ f'"{workflow.__workflow_id__}": All dependents maintained')
258
+ # check_state
259
+ if check_state(workflow, code_path, state_path) and not run:
260
+ logger.debug(
261
+ f'"{workflow.__workflow_id__}": In spec, no need to maintain')
262
+ return
263
+ # check_data
264
+ result = check_data(workflow, code_path, state_path, plot, session_id)
265
+ if result.in_spec:
266
+ if not run:
267
+ logger.debug(
268
+ f'"{workflow.__workflow_id__}": In spec, no need to maintain')
269
+ return
270
+ elif result.bad_data:
271
+ logger.debug(
272
+ f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
273
+ for n in get_dependents(workflow, code_path):
274
+ logger.debug(
275
+ f'diagnose "{n.__workflow_id__}" because of "{workflow.__workflow_id__}" bad data'
276
+ )
277
+ diagnose(n, code_path, state_path, plot, session_id)
278
+ else:
279
+ logger.debug(
280
+ f'"{workflow.__workflow_id__}": All dependents diagnosed')
281
+ # calibrate
282
+ logger.debug(f'recalibrate "{workflow.__workflow_id__}"')
283
+ result = calibrate(workflow, code_path, state_path, plot, session_id)
284
+ if result.bad_data or not result.in_spec:
285
+ raise CalibrationFailedError(
286
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
287
+ )
288
+ transform.update_parameters(result)
289
+ return
290
+
291
+
292
+ @logger.catch(reraise=True)
293
+ def run(workflow: WorkflowType,
294
+ code_path: str | Path,
295
+ state_path: str | Path,
296
+ plot: bool = False):
297
+ logger.debug(f'run "{workflow.__workflow_id__}" without dependences.')
298
+ result = calibrate(workflow, code_path, state_path, plot)
299
+ if result.bad_data or not result.in_spec:
300
+ raise CalibrationFailedError(
301
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
302
+ )
303
+ transform.update_parameters(result)
304
+ return
@@ -0,0 +1 @@
1
+ __version__ = "2.4.11"
@@ -1,264 +0,0 @@
1
- import functools
2
- import uuid
3
- from datetime import datetime, timedelta
4
- from pathlib import Path
5
-
6
- from loguru import logger
7
-
8
- from . import transform
9
- from .load import load_workflow
10
- from .storage import (Result, find_result, get_head, renew_result,
11
- revoke_result, save_result)
12
-
13
-
14
- class CalibrationFailedError(Exception):
15
- pass
16
-
17
-
18
- def check_state(workflow: str, code_path: str | Path,
19
- state_path: str | Path) -> bool:
20
- """
21
- check state should report a pass if and only if the following are satisfied:
22
-
23
- 1. The cal has had check data or calibrate pass within the timeout period.
24
- 2. The cal has not failed calibrate without resolution.
25
- 3. No dependencies have been recalibrated since the last time check data or calibrate was run on this cal.
26
- 4. All dependencies pass check state.
27
- """
28
- logger.debug(f'check_state: "{workflow}"')
29
- result = find_result(workflow, state_path)
30
- if not result:
31
- logger.debug(f'check_state failed: No history found for "{workflow}"')
32
- return False
33
- node = load_workflow(workflow, code_path)
34
- if hasattr(node, 'check_state') and callable(node.check_state):
35
- logger.debug(
36
- f'check_state: "{workflow}" has custom check_state method')
37
- return node.check_state(result)
38
- if node.__timeout__ is not None and datetime.now(
39
- ) > result.checked_time + timedelta(seconds=node.__timeout__):
40
- logger.debug(f'check_state failed: "{workflow}" has expired')
41
- return False
42
- if not result.in_spec:
43
- logger.debug(f'check_state failed: "{workflow}" is out of spec')
44
- return False
45
- if result.bad_data:
46
- logger.debug(f'check_state failed: "{workflow}" has bad data')
47
- return False
48
- for n in get_dependents(workflow, code_path):
49
- r = find_result(n, state_path)
50
- if r is None or r.checked_time > result.checked_time:
51
- logger.debug(
52
- f'check_state failed: "{workflow}" has outdated dependencies')
53
- return False
54
- for n in get_dependents(workflow, code_path):
55
- if not check_state(n, code_path, state_path):
56
- logger.debug(
57
- f'check_state failed: "{workflow}" has bad dependencies')
58
- return False
59
- return True
60
-
61
-
62
- def call_analyzer(node, data, history, check=False, plot=False):
63
- if check:
64
- result = transform.params_to_result(
65
- node.check_analyze(*data,
66
- history=transform.result_to_params(history)))
67
- result.fully_calibrated = False
68
- else:
69
- result = transform.params_to_result(
70
- node.analyze(*data, history=transform.result_to_params(history)))
71
- result.fully_calibrated = True
72
- if plot:
73
- call_plot(node, result)
74
- result.data = data
75
- return result
76
-
77
-
78
- @logger.catch()
79
- def call_plot(node, result, check=False):
80
- if hasattr(node, 'plot') and callable(node.plot):
81
- state, params, other = transform.result_to_params(result)
82
- node.plot(state, params, other)
83
-
84
-
85
- @functools.lru_cache(maxsize=128)
86
- def check_data(workflow: str, code_path: str | Path, state_path: str | Path,
87
- plot: bool, session_id: str) -> Result:
88
- """
89
- check data answers two questions:
90
- Is the parameter associated with this cal in spec,
91
- and is the cal scan working as expected?
92
- """
93
- node = load_workflow(workflow, code_path)
94
- history = find_result(workflow, state_path)
95
-
96
- if history is None:
97
- logger.debug(f'No history found for "{workflow}"')
98
- result = Result()
99
- result.in_spec = False
100
- result.bad_data = False
101
- return result
102
-
103
- if history.bad_data:
104
- logger.debug(f'History found for "{workflow}", but bad data')
105
- return history
106
- if not history.in_spec:
107
- logger.debug(f'History found for "{workflow}", but out of spec')
108
- return history
109
-
110
- logger.debug(f'History found for "{workflow}", but has expired')
111
-
112
- if hasattr(node, 'check') and callable(node.check) and hasattr(
113
- node, 'check_analyze') and callable(node.check_analyze):
114
- logger.debug(f'Checking "{workflow}" with "check" method ...')
115
- data = node.check()
116
- result = Result()
117
- result.data = data
118
- save_result(workflow, result, state_path)
119
-
120
- logger.debug(f'Checked "{workflow}" !')
121
- result = call_analyzer(node, data, history, check=True, plot=plot)
122
- if result.in_spec:
123
- logger.debug(f'"{workflow}": checked in spec, renewing result')
124
- renew_result(workflow, state_path)
125
- else:
126
- logger.debug(f'"{workflow}": checked out of spec, revoking result')
127
- revoke_result(workflow, state_path)
128
- else:
129
- logger.debug(f'Checking "{workflow}" with "calibrate" method ...')
130
- data = node.calibrate()
131
- result = Result()
132
- result.data = data
133
- save_result(workflow, result, state_path)
134
-
135
- logger.debug(f'Calibrated "{workflow}" !')
136
- result = call_analyzer(node, data, history, check=False, plot=plot)
137
- save_result(workflow, result, state_path,
138
- get_head(workflow, state_path))
139
-
140
- return result
141
-
142
-
143
- @functools.lru_cache(maxsize=128)
144
- def calibrate(workflow, code_path: str | Path, state_path: str | Path,
145
- plot: bool, session_id: str) -> Result:
146
- node = load_workflow(workflow, code_path)
147
- history = find_result(workflow, state_path)
148
-
149
- logger.debug(f'Calibrating "{workflow}" ...')
150
- data = node.calibrate()
151
- result = Result()
152
- result.data = data
153
- save_result(workflow, result, state_path)
154
- logger.debug(f'Calibrated "{workflow}" !')
155
- result = call_analyzer(node, data, history, check=False, plot=plot)
156
- save_result(workflow, result, state_path, get_head(workflow, state_path))
157
- return result
158
-
159
-
160
- def diagnose(node, code_path: str | Path, state_path: str | Path, plot: bool,
161
- session_id: str):
162
- '''
163
- Returns: True if node or dependent recalibrated.
164
- '''
165
- logger.debug(f'diagnose "{node}"')
166
- # check_data
167
- result = check_data(node, code_path, state_path, plot, session_id)
168
- # in spec case
169
- if result.in_spec:
170
- logger.debug(f'"{node}": Checked! In spec, no need to diagnose')
171
- return False
172
- # bad data case
173
- recalibrated = []
174
- if result.bad_data:
175
- logger.debug(f'"{node}": Bad data, diagnosing dependents')
176
- recalibrated = [
177
- diagnose(n, code_path, state_path, plot, session_id)
178
- for n in get_dependents(node, code_path)
179
- ]
180
- if not any(recalibrated):
181
- if result.bad_data:
182
- raise CalibrationFailedError(
183
- f'"{node}": bad data but no dependents recalibrated.')
184
- logger.debug(f'"{node}": no dependents recalibrated.')
185
- # calibrate
186
- if any(recalibrated):
187
- logger.debug(
188
- f'recalibrate "{node}" because some dependents recalibrated.')
189
- elif not result.in_spec and not result.bad_data:
190
- logger.debug(f'recalibrate "{node}" because out of spec.')
191
- elif result.in_spec:
192
- logger.error(f'Never reach: recalibrate "{node}" because in spec.')
193
- elif result.bad_data:
194
- logger.error(f'Never reach: recalibrate "{node}" because bad data.')
195
- else:
196
- logger.error(f'Never reach: recalibrate "{node}"')
197
-
198
- result = calibrate(node, code_path, state_path, plot, session_id)
199
- if result.bad_data or not result.in_spec:
200
- raise CalibrationFailedError(
201
- f'"{node}": All dependents passed, but calibration failed!')
202
- transform.update_parameters(result)
203
- return True
204
-
205
-
206
- def get_dependents(workflow: str, code_path: str | Path) -> list[str]:
207
- return [n for n in load_workflow(workflow, code_path).depends()[0]]
208
-
209
-
210
- @logger.catch(reraise=True)
211
- def maintain(node,
212
- code_path: str | Path,
213
- state_path: str | Path,
214
- session_id: str | None = None,
215
- run: bool = False,
216
- plot: bool = False):
217
- if session_id is None:
218
- session_id = uuid.uuid4().hex
219
- logger.debug(f'run "{node}"' if run else f'maintain "{node}"')
220
- # recursive maintain
221
- for n in get_dependents(node, code_path):
222
- logger.debug(f'maintain "{n}" because it is depended by "{node}"')
223
- maintain(n, code_path, state_path, session_id, run=False, plot=plot)
224
- else:
225
- logger.debug(f'"{node}": All dependents maintained')
226
- # check_state
227
- if check_state(node, code_path, state_path) and not run:
228
- logger.debug(f'"{node}": In spec, no need to maintain')
229
- return
230
- # check_data
231
- result = check_data(node, code_path, state_path, plot, session_id)
232
- if result.in_spec:
233
- if not run:
234
- logger.debug(f'"{node}": In spec, no need to maintain')
235
- return
236
- elif result.bad_data:
237
- logger.debug(f'"{node}": Bad data, diagnosing dependents')
238
- for n in get_dependents(node, code_path):
239
- logger.debug(f'diagnose "{n}" because of "{node}" bad data')
240
- diagnose(n, code_path, state_path, plot, session_id)
241
- else:
242
- logger.debug(f'"{node}": All dependents diagnosed')
243
- # calibrate
244
- logger.debug(f'recalibrate "{node}"')
245
- result = calibrate(node, code_path, state_path, plot, session_id)
246
- if result.bad_data or not result.in_spec:
247
- raise CalibrationFailedError(
248
- f'"{node}": All dependents passed, but calibration failed!')
249
- transform.update_parameters(result)
250
- return
251
-
252
-
253
- @logger.catch(reraise=True)
254
- def run(node,
255
- code_path: str | Path,
256
- state_path: str | Path,
257
- plot: bool = False):
258
- logger.debug(f'run "{node}" without dependences.')
259
- result = calibrate(node, code_path, state_path, plot)
260
- if result.bad_data or not result.in_spec:
261
- raise CalibrationFailedError(
262
- f'"{node}": All dependents passed, but calibration failed!')
263
- transform.update_parameters(result)
264
- return
@@ -1 +0,0 @@
1
- __version__ = "2.4.10"
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