QuLab 2.4.19__tar.gz → 2.5.0__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.
- {qulab-2.4.19 → qulab-2.5.0}/PKG-INFO +1 -1
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/PKG-INFO +1 -1
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/SOURCES.txt +1 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/__init__.py +2 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/__main__.py +1 -1
- {qulab-2.4.19 → qulab-2.5.0}/qulab/cli/config.py +26 -18
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/load.py +1 -1
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/schedule.py +11 -9
- qulab-2.5.0/qulab/executor/storage.py +191 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/utils.py +2 -3
- qulab-2.5.0/qulab/sys/rpc/worker.py +0 -0
- qulab-2.5.0/qulab/version.py +1 -0
- qulab-2.4.19/qulab/executor/storage.py +0 -147
- qulab-2.4.19/qulab/version.py +0 -1
- {qulab-2.4.19 → qulab-2.5.0}/LICENSE +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/MANIFEST.in +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/dependency_links.txt +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/entry_points.txt +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/requires.txt +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/QuLab.egg-info/top_level.txt +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/README.md +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/pyproject.toml +0 -0
- {qulab-2.4.19/qulab/storage → qulab-2.5.0/qulab/cli}/__init__.py +0 -0
- /qulab-2.4.19/qulab/cli/__init__.py → /qulab-2.5.0/qulab/cli/commands.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/dicttree.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/cli.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/executor/transform.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/__main__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/config.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/dataset.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/event_queue.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/mainwindow.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/monitor.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/ploter.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/qt_compat.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/monitor/toolbar.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/curd.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/expression.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/models.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/optimize.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/query.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/record.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/scan.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/server.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/space.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/scan/utils.py +0 -0
- {qulab-2.4.19/qulab/storage/backend → qulab-2.5.0/qulab/storage}/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/__main__.py +0 -0
- {qulab-2.4.19/qulab/sys → qulab-2.5.0/qulab/storage/backend}/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/backend/redis.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/base_dataset.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/chunk.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/dataset.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/file.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/base.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/config.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/file.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/ipy.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/models.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/record.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/report.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/models/tag.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/storage/storage.py +0 -0
- {qulab-2.4.19/qulab/sys/drivers → qulab-2.5.0/qulab/sys}/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/chat.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/device/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/device/basedevice.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/device/loader.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/device/utils.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/drivers/FakeInstrument.py +0 -0
- {qulab-2.4.19/qulab/sys/net → qulab-2.5.0/qulab/sys/drivers}/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/ipy_events.py +0 -0
- {qulab-2.4.19/qulab/sys/rpc → qulab-2.5.0/qulab/sys/net}/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/bencoder.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/cli.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/dhcp.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/dhcpd.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/kad.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/kcp.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/net/nginx.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/progress.py +0 -0
- /qulab-2.4.19/qulab/sys/rpc/client.py → /qulab-2.5.0/qulab/sys/rpc/__init__.py +0 -0
- /qulab-2.4.19/qulab/sys/rpc/worker.py → /qulab-2.5.0/qulab/sys/rpc/client.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/exceptions.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/msgpack.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/msgpack.pyi +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/router.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/rpc.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/serialize.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/server.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/socket.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/utils.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/sys/rpc/zmq_socket.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/__init__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/__main__.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/_autoplot.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/plot_circ.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/plot_layout.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/plot_seq.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/qdat.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/rot3d.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/qulab/visualization/widgets.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/setup.cfg +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/setup.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/src/qulab.h +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/tests/test_kad.py +0 -0
- {qulab-2.4.19 → qulab-2.5.0}/tests/test_scan.py +0 -0
|
@@ -25,7 +25,7 @@ def _get_config_value(option_name, type_cast=str, command_name=None):
|
|
|
25
25
|
if env_value := os.getenv(env_var):
|
|
26
26
|
if type_cast is bool:
|
|
27
27
|
return env_value.lower() in ("true", "1", "yes")
|
|
28
|
-
if "path" in option_name:
|
|
28
|
+
if "path" in option_name or issubclass(type_cast, Path):
|
|
29
29
|
return os.path.expanduser(env_value)
|
|
30
30
|
return type_cast(env_value)
|
|
31
31
|
|
|
@@ -43,7 +43,7 @@ def _get_config_value(option_name, type_cast=str, command_name=None):
|
|
|
43
43
|
fallback=None):
|
|
44
44
|
if type_cast is bool:
|
|
45
45
|
return config_value.lower() in ("true", "1", "yes")
|
|
46
|
-
if "path" in option_name:
|
|
46
|
+
if "path" in option_name or issubclass(type_cast, Path):
|
|
47
47
|
return os.path.expanduser(config_value)
|
|
48
48
|
return type_cast(config_value)
|
|
49
49
|
|
|
@@ -62,27 +62,35 @@ def log_options(func):
|
|
|
62
62
|
|
|
63
63
|
@click.option("--debug",
|
|
64
64
|
is_flag=True,
|
|
65
|
-
default=
|
|
65
|
+
default=get_config_value("debug", bool),
|
|
66
66
|
help=f"Enable debug mode")
|
|
67
67
|
@click.option("--log",
|
|
68
68
|
type=click.Path(),
|
|
69
|
-
default=lambda: get_config_value("log"),
|
|
69
|
+
default=lambda: get_config_value("log", Path),
|
|
70
70
|
help=f"Log file path")
|
|
71
|
+
@click.option("--quiet",
|
|
72
|
+
is_flag=True,
|
|
73
|
+
default=get_config_value("quiet", bool),
|
|
74
|
+
help=f"Disable log output")
|
|
71
75
|
@functools.wraps(func)
|
|
72
|
-
def wrapper(*args,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
def wrapper(*args, **kwargs):
|
|
77
|
+
debug = bool(kwargs.pop("debug"))
|
|
78
|
+
log = kwargs.pop("log")
|
|
79
|
+
quiet = bool(kwargs.pop("quiet"))
|
|
80
|
+
|
|
81
|
+
if debug:
|
|
82
|
+
log_level = "DEBUG"
|
|
83
|
+
else:
|
|
84
|
+
log_level = "INFO"
|
|
85
|
+
|
|
86
|
+
handlers = []
|
|
87
|
+
if log is not None:
|
|
88
|
+
handlers.append(dict(sink=log, level=log_level))
|
|
89
|
+
if not quiet or debug:
|
|
90
|
+
handlers.append(dict(sink=sys.stderr, level=log_level))
|
|
91
|
+
|
|
92
|
+
logger.configure(handlers=handlers)
|
|
93
|
+
|
|
86
94
|
return func(*args, **kwargs)
|
|
87
95
|
|
|
88
96
|
return wrapper
|
|
@@ -269,7 +269,7 @@ def load_workflow_from_template(template_path: str,
|
|
|
269
269
|
str: 已经替换的新字符串。
|
|
270
270
|
"""
|
|
271
271
|
pattern = re.compile(r'VAR\s*\(\s*(["\'])(\w+)\1\s*\)')
|
|
272
|
-
replacement = f'__VAR_{hash_str}' + r'[
|
|
272
|
+
replacement = f'__VAR_{hash_str}' + r'[\1\2\1]'
|
|
273
273
|
new_text = re.sub(pattern, replacement, text)
|
|
274
274
|
return new_text
|
|
275
275
|
|
|
@@ -7,8 +7,8 @@ from loguru import logger
|
|
|
7
7
|
|
|
8
8
|
from . import transform
|
|
9
9
|
from .load import WorkflowType, get_dependents
|
|
10
|
-
from .storage import (Result, find_result,
|
|
11
|
-
|
|
10
|
+
from .storage import (Result, find_result, renew_result, revoke_result,
|
|
11
|
+
save_result)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class CalibrationFailedError(Exception):
|
|
@@ -120,13 +120,14 @@ def check_data(workflow: WorkflowType, code_path: str | Path,
|
|
|
120
120
|
|
|
121
121
|
if hasattr(workflow, 'check') and callable(workflow.check) and hasattr(
|
|
122
122
|
workflow, 'check_analyze') and callable(workflow.check_analyze):
|
|
123
|
-
logger.debug(
|
|
123
|
+
logger.debug(
|
|
124
|
+
f'Checking "{workflow.__workflow_id__}" with "check" method ...')
|
|
124
125
|
data = workflow.check()
|
|
125
126
|
result = Result()
|
|
126
127
|
result.data = data
|
|
127
|
-
save_result(workflow.__workflow_id__, result, state_path)
|
|
128
|
+
#save_result(workflow.__workflow_id__, result, state_path)
|
|
128
129
|
|
|
129
|
-
logger.debug(f'Checked "{workflow}" !')
|
|
130
|
+
logger.debug(f'Checked "{workflow.__workflow_id__}" !')
|
|
130
131
|
result = call_analyzer(workflow, data, history, check=True, plot=plot)
|
|
131
132
|
if result.in_spec:
|
|
132
133
|
logger.debug(
|
|
@@ -149,8 +150,10 @@ def check_data(workflow: WorkflowType, code_path: str | Path,
|
|
|
149
150
|
|
|
150
151
|
logger.debug(f'Calibrated "{workflow}" !')
|
|
151
152
|
result = call_analyzer(workflow, data, history, check=False, plot=plot)
|
|
152
|
-
save_result(workflow.__workflow_id__,
|
|
153
|
-
|
|
153
|
+
save_result(workflow.__workflow_id__,
|
|
154
|
+
result,
|
|
155
|
+
state_path,
|
|
156
|
+
overwrite=True)
|
|
154
157
|
|
|
155
158
|
return result
|
|
156
159
|
|
|
@@ -167,8 +170,7 @@ def calibrate(workflow: WorkflowType, code_path: str | Path,
|
|
|
167
170
|
save_result(workflow.__workflow_id__, result, state_path)
|
|
168
171
|
logger.debug(f'Calibrated "{workflow.__workflow_id__}" !')
|
|
169
172
|
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))
|
|
173
|
+
save_result(workflow.__workflow_id__, result, state_path, overwrite=True)
|
|
172
174
|
return result
|
|
173
175
|
|
|
174
176
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import lzma
|
|
2
|
+
import pickle
|
|
3
|
+
import uuid
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from ..cli.config import get_config_value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Result():
|
|
15
|
+
in_spec: bool = False
|
|
16
|
+
bad_data: bool = False
|
|
17
|
+
fully_calibrated: bool = False
|
|
18
|
+
calibrated_time: datetime = field(default_factory=datetime.now)
|
|
19
|
+
checked_time: datetime = field(default_factory=datetime.now)
|
|
20
|
+
ttl: timedelta = timedelta(days=3650)
|
|
21
|
+
params: dict = field(default_factory=dict)
|
|
22
|
+
info: dict = field(default_factory=dict)
|
|
23
|
+
data: tuple = field(default_factory=tuple)
|
|
24
|
+
index: int = -1
|
|
25
|
+
previous_path: Path | None = None
|
|
26
|
+
base_path: Path | None = None
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def previous(self):
|
|
30
|
+
if self.previous_path is not None and self.base_path is not None:
|
|
31
|
+
return load_result(self.previous_path, self.base_path)
|
|
32
|
+
else:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def random_path(base: Path) -> Path:
|
|
37
|
+
while True:
|
|
38
|
+
s = uuid.uuid4().hex
|
|
39
|
+
path = Path(s[:2]) / s[2:4] / s[4:6] / s[6:]
|
|
40
|
+
if not (base / path).exists():
|
|
41
|
+
return path
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def save_result(workflow: str,
|
|
45
|
+
result: Result,
|
|
46
|
+
base_path: str | Path,
|
|
47
|
+
overwrite: bool = False):
|
|
48
|
+
logger.debug(
|
|
49
|
+
f'Saving result for "{workflow}", {result.in_spec=}, {result.bad_data=}, {result.fully_calibrated=}'
|
|
50
|
+
)
|
|
51
|
+
base_path = Path(base_path)
|
|
52
|
+
if overwrite:
|
|
53
|
+
buf = lzma.compress(pickle.dumps(result))
|
|
54
|
+
path = get_head(workflow, base_path)
|
|
55
|
+
with open(base_path / 'objects' / path, "rb") as f:
|
|
56
|
+
index = int.from_bytes(f.read(8), 'big')
|
|
57
|
+
result.index = index
|
|
58
|
+
else:
|
|
59
|
+
result.previous_path = get_head(workflow, base_path)
|
|
60
|
+
buf = lzma.compress(pickle.dumps(result))
|
|
61
|
+
path = random_path(base_path / 'objects')
|
|
62
|
+
(base_path / 'objects' / path).parent.mkdir(parents=True,
|
|
63
|
+
exist_ok=True)
|
|
64
|
+
result.index = create_index("result",
|
|
65
|
+
base_path,
|
|
66
|
+
context=str(path),
|
|
67
|
+
width=35)
|
|
68
|
+
|
|
69
|
+
with open(base_path / 'objects' / path, "wb") as f:
|
|
70
|
+
f.write(result.index.to_bytes(8, 'big'))
|
|
71
|
+
f.write(buf)
|
|
72
|
+
set_head(workflow, path, base_path)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def load_result(path: str | Path, base_path: str | Path) -> Result | None:
|
|
76
|
+
base_path = Path(base_path)
|
|
77
|
+
path = base_path / 'objects' / path
|
|
78
|
+
|
|
79
|
+
with open(base_path / 'objects' / path, "rb") as f:
|
|
80
|
+
index = int.from_bytes(f.read(8), 'big')
|
|
81
|
+
result = pickle.loads(lzma.decompress(f.read()))
|
|
82
|
+
result.base_path = base_path
|
|
83
|
+
result.index = index
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def find_result(
|
|
88
|
+
workflow: str, base_path: str | Path = get_config_value("data", Path)
|
|
89
|
+
) -> Result | None:
|
|
90
|
+
base_path = Path(base_path)
|
|
91
|
+
path = get_head(workflow, base_path)
|
|
92
|
+
if path is None:
|
|
93
|
+
return None
|
|
94
|
+
return load_result(path, base_path)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def renew_result(workflow: str, base_path: str | Path):
|
|
98
|
+
logger.debug(f'Renewing result for "{workflow}"')
|
|
99
|
+
result = find_result(workflow, base_path)
|
|
100
|
+
if result is not None:
|
|
101
|
+
result.checked_time = datetime.now()
|
|
102
|
+
save_result(workflow, result, base_path)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def revoke_result(workflow: str, base_path: str | Path):
|
|
106
|
+
logger.debug(f'Revoking result for "{workflow}"')
|
|
107
|
+
base_path = Path(base_path)
|
|
108
|
+
path = get_head(workflow, base_path)
|
|
109
|
+
if path is not None:
|
|
110
|
+
with open(base_path / 'objects' / path, "rb") as f:
|
|
111
|
+
result = pickle.load(f)
|
|
112
|
+
result.in_spec = False
|
|
113
|
+
save_result(workflow, result, base_path)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def set_head(workflow: str, path: Path, base_path: str | Path):
|
|
117
|
+
base_path = Path(base_path)
|
|
118
|
+
base_path.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
try:
|
|
120
|
+
with open(base_path / "heads", "rb") as f:
|
|
121
|
+
heads = pickle.load(f)
|
|
122
|
+
except:
|
|
123
|
+
heads = {}
|
|
124
|
+
heads[workflow] = path
|
|
125
|
+
with open(base_path / "heads", "wb") as f:
|
|
126
|
+
pickle.dump(heads, f)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_head(workflow: str, base_path: str | Path) -> Path | None:
|
|
130
|
+
base_path = Path(base_path)
|
|
131
|
+
try:
|
|
132
|
+
with open(base_path / "heads", "rb") as f:
|
|
133
|
+
heads = pickle.load(f)
|
|
134
|
+
return heads[workflow]
|
|
135
|
+
except:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_heads(base_path: str | Path) -> Path | None:
|
|
140
|
+
base_path = Path(base_path)
|
|
141
|
+
try:
|
|
142
|
+
with open(base_path / "heads", "rb") as f:
|
|
143
|
+
heads = pickle.load(f)
|
|
144
|
+
return heads
|
|
145
|
+
except:
|
|
146
|
+
return {}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def create_index(name: str,
|
|
150
|
+
base_path: str | Path,
|
|
151
|
+
context: str,
|
|
152
|
+
width: int,
|
|
153
|
+
start: int = 0):
|
|
154
|
+
path = Path(base_path) / "index" / f"{name}.seq"
|
|
155
|
+
if path.exists():
|
|
156
|
+
with open(path, "r") as f:
|
|
157
|
+
index = int(f.read())
|
|
158
|
+
else:
|
|
159
|
+
index = start
|
|
160
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
with open(path, "w") as f:
|
|
162
|
+
f.write(str(index + 1))
|
|
163
|
+
|
|
164
|
+
path = Path(base_path) / "index" / f"{name}.width"
|
|
165
|
+
with open(path, "w") as f:
|
|
166
|
+
f.write(str(width))
|
|
167
|
+
|
|
168
|
+
path = Path(base_path) / "index" / f"{name}.idx"
|
|
169
|
+
with open(path, "a") as f:
|
|
170
|
+
|
|
171
|
+
f.write(f"{context.ljust(width)}\n")
|
|
172
|
+
|
|
173
|
+
return index
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def query_index(name: str, base_path: str | Path, index: int):
|
|
177
|
+
path = Path(base_path) / "index" / f"{name}.width"
|
|
178
|
+
with open(path, "r") as f:
|
|
179
|
+
width = int(f.read())
|
|
180
|
+
path = Path(base_path) / "index" / f"{name}.idx"
|
|
181
|
+
with open(path, "r") as f:
|
|
182
|
+
f.seek(index * (width + 1))
|
|
183
|
+
context = f.read(width)
|
|
184
|
+
return context.rstrip()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_result_by_index(
|
|
188
|
+
index: int, base_path: str | Path = get_config_value("data", Path)
|
|
189
|
+
) -> Result | None:
|
|
190
|
+
path = query_index("result", base_path, index)
|
|
191
|
+
return load_result(path, base_path)
|
|
@@ -31,10 +31,9 @@ def dependent_tree(node: str, code_path: str | Path) -> dict[str, list[str]]:
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def workflow_template(deps: list[str]) -> str:
|
|
34
|
-
return f"""
|
|
35
|
-
from loguru import logger
|
|
34
|
+
return f"""from loguru import logger
|
|
36
35
|
|
|
37
|
-
def VAR(s): pass
|
|
36
|
+
def VAR(s): pass # 没有实际作用,只是用来抑制编辑器的警告。
|
|
38
37
|
|
|
39
38
|
import numpy as np
|
|
40
39
|
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.5.0"
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import pickle
|
|
2
|
-
import uuid
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from datetime import datetime, timedelta
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from loguru import logger
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class Result():
|
|
12
|
-
in_spec: bool = False
|
|
13
|
-
bad_data: bool = False
|
|
14
|
-
fully_calibrated: bool = False
|
|
15
|
-
calibrated_time: datetime = field(default_factory=datetime.now)
|
|
16
|
-
checked_time: datetime = field(default_factory=datetime.now)
|
|
17
|
-
ttl: timedelta = timedelta(days=3650)
|
|
18
|
-
params: dict = field(default_factory=dict)
|
|
19
|
-
info: dict = field(default_factory=dict)
|
|
20
|
-
data: tuple = field(default_factory=tuple)
|
|
21
|
-
previous: Path | None = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Graph:
|
|
25
|
-
|
|
26
|
-
def __init__(self):
|
|
27
|
-
self.nodes = {}
|
|
28
|
-
self.heads = set()
|
|
29
|
-
self.roots = set()
|
|
30
|
-
|
|
31
|
-
def add_node(self, node: str, deps: list[str]):
|
|
32
|
-
if node not in self.nodes:
|
|
33
|
-
self.nodes[node] = deps
|
|
34
|
-
if not deps:
|
|
35
|
-
self.heads.add(node)
|
|
36
|
-
for dep in deps:
|
|
37
|
-
if dep not in self.nodes:
|
|
38
|
-
self.nodes[dep] = []
|
|
39
|
-
self.roots.discard(dep)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def random_path(base: Path) -> Path:
|
|
43
|
-
while True:
|
|
44
|
-
s = uuid.uuid4().hex
|
|
45
|
-
path = Path(s[:2]) / s[2:4] / s[4:6] / s[6:]
|
|
46
|
-
if not (base / path).exists():
|
|
47
|
-
return path
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def save_result(workflow: str,
|
|
51
|
-
result: Result,
|
|
52
|
-
base_path: str | Path,
|
|
53
|
-
path: Path | None = None):
|
|
54
|
-
logger.debug(
|
|
55
|
-
f'Saving result for "{workflow}", {result.in_spec=}, {result.bad_data=}, {result.fully_calibrated=}'
|
|
56
|
-
)
|
|
57
|
-
base_path = Path(base_path)
|
|
58
|
-
if path is None:
|
|
59
|
-
path = random_path(base_path)
|
|
60
|
-
(base_path / 'objects' / path).parent.mkdir(parents=True, exist_ok=True)
|
|
61
|
-
result.previous = get_head(workflow, base_path)
|
|
62
|
-
with open(base_path / 'objects' / path, "wb") as f:
|
|
63
|
-
pickle.dump(result, f)
|
|
64
|
-
set_head(workflow, path, base_path)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def find_result(workflow: str, base_path: str | Path) -> Result | None:
|
|
68
|
-
base_path = Path(base_path)
|
|
69
|
-
path = get_head(workflow, base_path)
|
|
70
|
-
if path is None:
|
|
71
|
-
return None
|
|
72
|
-
with open(base_path / 'objects' / path, "rb") as f:
|
|
73
|
-
return pickle.load(f)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def renew_result(workflow: str, base_path: str | Path):
|
|
77
|
-
logger.debug(f'Renewing result for "{workflow}"')
|
|
78
|
-
result = find_result(workflow, base_path)
|
|
79
|
-
if result is not None:
|
|
80
|
-
result.checked_time = datetime.now()
|
|
81
|
-
save_result(workflow, result, base_path)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def revoke_result(workflow: str, base_path: str | Path):
|
|
85
|
-
logger.debug(f'Revoking result for "{workflow}"')
|
|
86
|
-
base_path = Path(base_path)
|
|
87
|
-
path = get_head(workflow, base_path)
|
|
88
|
-
if path is not None:
|
|
89
|
-
with open(base_path / 'objects' / path, "rb") as f:
|
|
90
|
-
result = pickle.load(f)
|
|
91
|
-
result.in_spec = False
|
|
92
|
-
save_result(workflow, result, base_path)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def set_head(workflow: str, path: Path, base_path: str | Path):
|
|
96
|
-
base_path = Path(base_path)
|
|
97
|
-
base_path.mkdir(parents=True, exist_ok=True)
|
|
98
|
-
try:
|
|
99
|
-
with open(base_path / "heads", "rb") as f:
|
|
100
|
-
heads = pickle.load(f)
|
|
101
|
-
except:
|
|
102
|
-
heads = {}
|
|
103
|
-
heads[workflow] = path
|
|
104
|
-
with open(base_path / "heads", "wb") as f:
|
|
105
|
-
pickle.dump(heads, f)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def get_head(workflow: str, base_path: str | Path) -> Path | None:
|
|
109
|
-
base_path = Path(base_path)
|
|
110
|
-
try:
|
|
111
|
-
with open(base_path / "heads", "rb") as f:
|
|
112
|
-
heads = pickle.load(f)
|
|
113
|
-
return heads[workflow]
|
|
114
|
-
except:
|
|
115
|
-
return None
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def get_graph(base_path: str | Path) -> dict[str, list[str]]:
|
|
119
|
-
base_path = Path(base_path)
|
|
120
|
-
try:
|
|
121
|
-
with open(base_path / "heads", "rb") as f:
|
|
122
|
-
heads = pickle.load(f)
|
|
123
|
-
except:
|
|
124
|
-
heads = {}
|
|
125
|
-
graph = {}
|
|
126
|
-
for workflow, path in heads.items():
|
|
127
|
-
graph[workflow] = []
|
|
128
|
-
while path is not None:
|
|
129
|
-
with open(base_path / 'objects' / path, "rb") as f:
|
|
130
|
-
result = pickle.load(f)
|
|
131
|
-
path = result.previous
|
|
132
|
-
if path is not None:
|
|
133
|
-
graph[workflow].append(path)
|
|
134
|
-
return graph
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def update_graph(workflow: str, base_path: str | Path):
|
|
138
|
-
base_path = Path(base_path)
|
|
139
|
-
graph = get_graph(base_path)
|
|
140
|
-
for workflow, deps in graph.items():
|
|
141
|
-
for dep in deps:
|
|
142
|
-
if dep not in graph:
|
|
143
|
-
graph[dep] = []
|
|
144
|
-
if workflow not in graph[dep]:
|
|
145
|
-
graph[dep].append(workflow)
|
|
146
|
-
with open(base_path / "graph", "wb") as f:
|
|
147
|
-
pickle.dump(graph, f)
|
qulab-2.4.19/qulab/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.4.19"
|
|
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
|
|
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
|