QuLab 2.11.2__py3-none-any.whl → 2.11.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qulab/cli/commands.py +2 -1
- qulab/executor/cli.py +32 -9
- qulab/utils.py +49 -14
- qulab/version.py +1 -1
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/METADATA +1 -1
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/RECORD +10 -10
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/WHEEL +0 -0
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/entry_points.txt +0 -0
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/licenses/LICENSE +0 -0
- {qulab-2.11.2.dist-info → qulab-2.11.4.dist-info}/top_level.txt +0 -0
qulab/cli/commands.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import click
|
2
2
|
|
3
|
-
from ..executor.cli import (boot, create, export, get, load, maintain,
|
3
|
+
from ..executor.cli import (boot, create, export, get, load, maintain, delete,
|
4
4
|
reproduce, run, set)
|
5
5
|
from ..monitor.__main__ import main as monitor
|
6
6
|
from ..scan.server import server
|
@@ -35,6 +35,7 @@ cli.add_command(reproduce)
|
|
35
35
|
cli.add_command(create)
|
36
36
|
reg.add_command(set)
|
37
37
|
reg.add_command(get)
|
38
|
+
reg.add_command(delete)
|
38
39
|
reg.add_command(load)
|
39
40
|
reg.add_command(export)
|
40
41
|
cli.add_command(boot)
|
qulab/executor/cli.py
CHANGED
@@ -11,6 +11,7 @@ from loguru import logger
|
|
11
11
|
|
12
12
|
from ..cli.config import get_config_value, log_options
|
13
13
|
from ..cli.decorators import async_command
|
14
|
+
from ..utils import combined_env
|
14
15
|
from .load import (WorkflowType, find_unreferenced_workflows, get_entries,
|
15
16
|
load_workflow, make_graph)
|
16
17
|
from .registry import Registry, set_config_api
|
@@ -21,12 +22,14 @@ from .utils import workflow_template
|
|
21
22
|
|
22
23
|
|
23
24
|
@logger.catch(reraise=True)
|
24
|
-
def run_script(script_path):
|
25
|
-
"""Run a script in a new
|
25
|
+
def run_script(script_path, extra_paths=None):
|
26
|
+
"""Run a script in a new process, inheriting current PYTHONPATH plus any extra paths."""
|
26
27
|
import subprocess
|
27
28
|
import sys
|
28
29
|
|
29
|
-
|
30
|
+
# Launch the new process with the modified environment
|
31
|
+
proc = subprocess.Popen([sys.executable, script_path],
|
32
|
+
env=combined_env(extra_paths))
|
30
33
|
proc.communicate()
|
31
34
|
|
32
35
|
|
@@ -131,7 +134,7 @@ def set(key, value, api):
|
|
131
134
|
"""
|
132
135
|
Set a config.
|
133
136
|
"""
|
134
|
-
logger.info(f'[CMD]: set {key} {value} --api {api}')
|
137
|
+
logger.info(f'[CMD]: reg set {key} {value} --api {api}')
|
135
138
|
reg = Registry()
|
136
139
|
if api is not None:
|
137
140
|
api = importlib.import_module(api)
|
@@ -155,7 +158,7 @@ def get(key, api):
|
|
155
158
|
"""
|
156
159
|
Get a config.
|
157
160
|
"""
|
158
|
-
logger.info(f'[CMD]: get {key} --api {api}')
|
161
|
+
logger.info(f'[CMD]: reg get {key} --api {api}')
|
159
162
|
reg = Registry()
|
160
163
|
if api is not None:
|
161
164
|
api = importlib.import_module(api)
|
@@ -164,6 +167,26 @@ def get(key, api):
|
|
164
167
|
rich.print(reg.get(key))
|
165
168
|
|
166
169
|
|
170
|
+
@click.command()
|
171
|
+
@click.argument('key')
|
172
|
+
@click.option('--api',
|
173
|
+
'-a',
|
174
|
+
default=lambda: get_config_value("api", str, 'get'),
|
175
|
+
help='The modlule name of the api.')
|
176
|
+
@log_options('delete')
|
177
|
+
def delete(key, api):
|
178
|
+
"""
|
179
|
+
Delete a config key.
|
180
|
+
"""
|
181
|
+
logger.info(f'[CMD]: reg delete {key} --api {api}')
|
182
|
+
reg = Registry()
|
183
|
+
if api is not None:
|
184
|
+
api = importlib.import_module(api)
|
185
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
186
|
+
api.export_config, api.clear_config)
|
187
|
+
reg.delete(key)
|
188
|
+
|
189
|
+
|
167
190
|
@click.command()
|
168
191
|
@click.argument('file')
|
169
192
|
@click.option('--api',
|
@@ -172,14 +195,14 @@ def get(key, api):
|
|
172
195
|
help='The modlule name of the api.')
|
173
196
|
@click.option('--format',
|
174
197
|
'-f',
|
175
|
-
default='
|
198
|
+
default='pickle',
|
176
199
|
help='The format of the config.')
|
177
200
|
@log_options('export')
|
178
201
|
def export(file, api, format):
|
179
202
|
"""
|
180
203
|
Export a config.
|
181
204
|
"""
|
182
|
-
logger.info(f'[CMD]: export {file} --api {api}')
|
205
|
+
logger.info(f'[CMD]: reg export {file} --api {api}')
|
183
206
|
reg = Registry()
|
184
207
|
if api is not None:
|
185
208
|
api = importlib.import_module(api)
|
@@ -210,14 +233,14 @@ def export(file, api, format):
|
|
210
233
|
help='The modlule name of the api.')
|
211
234
|
@click.option('--format',
|
212
235
|
'-f',
|
213
|
-
default='
|
236
|
+
default='pickle',
|
214
237
|
help='The format of the config.')
|
215
238
|
@log_options('load')
|
216
239
|
def load(file, api, format):
|
217
240
|
"""
|
218
241
|
Load a config.
|
219
242
|
"""
|
220
|
-
logger.info(f'[CMD]: load {file} --api {api}')
|
243
|
+
logger.info(f'[CMD]: reg load {file} --api {api}')
|
221
244
|
reg = Registry()
|
222
245
|
if api is not None:
|
223
246
|
api = importlib.import_module(api)
|
qulab/utils.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
import shlex
|
2
3
|
import subprocess
|
3
4
|
import sys
|
@@ -6,36 +7,56 @@ import time
|
|
6
7
|
import click
|
7
8
|
|
8
9
|
|
9
|
-
def
|
10
|
+
def combined_env(extra_paths=None):
|
11
|
+
env = os.environ.copy()
|
12
|
+
|
13
|
+
# Build a combined PYTHONPATH: current interpreter's sys.path entries + extra_paths
|
14
|
+
paths = [p for p in sys.path if p]
|
15
|
+
if extra_paths:
|
16
|
+
if isinstance(extra_paths, str):
|
17
|
+
extra_paths = [extra_paths]
|
18
|
+
paths.extend(extra_paths)
|
19
|
+
|
20
|
+
# Prepend to any existing PYTHONPATH in the environment
|
21
|
+
existing = env.get('PYTHONPATH', '')
|
22
|
+
combined = os.pathsep.join(paths + ([existing] if existing else []))
|
23
|
+
env['PYTHONPATH'] = combined
|
24
|
+
return env
|
25
|
+
|
26
|
+
|
27
|
+
def run_detached(executable_path, env=None):
|
10
28
|
"""
|
11
29
|
启动可执行文件并完全分离(优先用 tmux/screen),无需额外终端窗口
|
12
30
|
支持 Windows、Linux 和 macOS
|
13
31
|
"""
|
32
|
+
if env is None:
|
33
|
+
env = combined_env()
|
14
34
|
try:
|
15
35
|
if sys.platform == 'win32' or not _unix_detach_with_tmux_or_screen(
|
16
|
-
executable_path):
|
36
|
+
executable_path, env):
|
17
37
|
# 回退到带终端窗口的方案
|
18
|
-
run_detached_with_terminal(executable_path)
|
38
|
+
run_detached_with_terminal(executable_path, env)
|
19
39
|
|
20
40
|
except Exception as e:
|
21
41
|
click.echo(f"启动失败: {e}")
|
22
42
|
sys.exit(1)
|
23
43
|
|
24
44
|
|
25
|
-
def _windows_start(executable_path):
|
45
|
+
def _windows_start(executable_path, env):
|
26
46
|
"""Windows 弹窗启动方案"""
|
27
47
|
subprocess.Popen(f'start cmd /k "{executable_path}"',
|
28
48
|
shell=True,
|
49
|
+
env=env,
|
29
50
|
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
30
51
|
|
31
52
|
|
32
|
-
def _unix_detach_with_tmux_or_screen(executable_path):
|
53
|
+
def _unix_detach_with_tmux_or_screen(executable_path, env):
|
33
54
|
"""Unix 后台分离方案(无窗口)"""
|
34
55
|
safe_path = shlex.quote(executable_path)
|
35
56
|
session_name = f"qulab_{int(time.time())}"
|
36
57
|
|
37
58
|
# 尝试 tmux
|
38
|
-
if _check_command_exists("tmux"):
|
59
|
+
if _check_command_exists("tmux", env):
|
39
60
|
command = [
|
40
61
|
"tmux",
|
41
62
|
"new-session",
|
@@ -48,15 +69,18 @@ def _unix_detach_with_tmux_or_screen(executable_path):
|
|
48
69
|
"wait-for",
|
49
70
|
"finished" # 防止进程立即退出
|
50
71
|
]
|
51
|
-
subprocess.Popen(" ".join(command),
|
72
|
+
subprocess.Popen(" ".join(command),
|
73
|
+
shell=True,
|
74
|
+
env=env,
|
75
|
+
start_new_session=True)
|
52
76
|
click.echo(f"已启动 tmux 会话: {session_name}")
|
53
77
|
click.echo(f"你可以使用 `tmux attach -t {session_name}` 来查看输出")
|
54
78
|
return True
|
55
79
|
|
56
80
|
# 尝试 screen
|
57
|
-
elif _check_command_exists("screen"):
|
81
|
+
elif _check_command_exists("screen", env):
|
58
82
|
command = ["screen", "-dmS", session_name, executable_path]
|
59
|
-
subprocess.Popen(command, start_new_session=True)
|
83
|
+
subprocess.Popen(command, env=env, start_new_session=True)
|
60
84
|
click.echo(f"已启动 screen 会话: {session_name}")
|
61
85
|
click.echo(f"你可以使用 `screen -r {session_name}` 来查看输出")
|
62
86
|
return True
|
@@ -64,27 +88,38 @@ def _unix_detach_with_tmux_or_screen(executable_path):
|
|
64
88
|
return False
|
65
89
|
|
66
90
|
|
67
|
-
def run_detached_with_terminal(executable_path):
|
91
|
+
def run_detached_with_terminal(executable_path, env=None):
|
68
92
|
"""回退到带终端窗口的方案"""
|
93
|
+
if env is None:
|
94
|
+
env = combined_env()
|
95
|
+
|
69
96
|
if sys.platform == 'win32':
|
70
|
-
_windows_start(executable_path)
|
97
|
+
_windows_start(executable_path, env)
|
71
98
|
elif sys.platform == 'darwin':
|
99
|
+
# executable_path=shlex.quote(executable_path)
|
100
|
+
print(executable_path)
|
72
101
|
script = f'tell app "Terminal" to do script "{executable_path}"'
|
73
|
-
subprocess.Popen(["osascript", "-e", script],
|
102
|
+
subprocess.Popen(["osascript", "-e", script],
|
103
|
+
env=env,
|
104
|
+
start_new_session=True)
|
74
105
|
else:
|
75
106
|
try:
|
76
107
|
subprocess.Popen(
|
77
108
|
["gnome-terminal", "--", "sh", "-c", executable_path],
|
109
|
+
env=env,
|
78
110
|
start_new_session=True)
|
79
111
|
except FileNotFoundError:
|
80
112
|
subprocess.Popen(["xterm", "-e", executable_path],
|
113
|
+
env=env,
|
81
114
|
start_new_session=True)
|
82
115
|
|
83
116
|
|
84
|
-
def _check_command_exists(cmd):
|
117
|
+
def _check_command_exists(cmd, env):
|
85
118
|
"""检查命令行工具是否存在"""
|
86
119
|
try:
|
87
|
-
subprocess.check_output(["which", cmd],
|
120
|
+
subprocess.check_output(["which", cmd],
|
121
|
+
env=env,
|
122
|
+
stderr=subprocess.DEVNULL)
|
88
123
|
return True
|
89
124
|
except:
|
90
125
|
return False
|
qulab/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "2.11.
|
1
|
+
__version__ = "2.11.4"
|
@@ -1,15 +1,15 @@
|
|
1
1
|
qulab/__init__.py,sha256=hmf6R3jiM5NtJY1mSptogYnH5DMTR2dTzlXykqLxQzg,2027
|
2
2
|
qulab/__main__.py,sha256=fjaRSL_uUjNIzBGNgjlGswb9TJ2VD5qnkZHW3hItrD4,68
|
3
3
|
qulab/typing.py,sha256=vg62sGqxuD9CI5677ejlzAmf2fVdAESZCQjAE_xSxPg,69
|
4
|
-
qulab/utils.py,sha256=
|
5
|
-
qulab/version.py,sha256=
|
4
|
+
qulab/utils.py,sha256=w5oSw9Ypux6l0oB-MzlQvT40iX_rzQ5wXcwzr7CIVf8,4107
|
5
|
+
qulab/version.py,sha256=R23vM4RbEfPLzmIiHDPzMMGKJcHvi00MAAor3IxzcCA,22
|
6
6
|
qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
qulab/cli/commands.py,sha256=
|
7
|
+
qulab/cli/commands.py,sha256=ywKmwbGNBCVASx8OsgrRttaLz9ogwY38ZdKj7I-tdJ4,813
|
8
8
|
qulab/cli/config.py,sha256=tZPSBLbf2x_Brb2UBuA1bsni8nC8WOPPGyWIi8m7j1I,5459
|
9
9
|
qulab/cli/decorators.py,sha256=oImteZVnDPPWdyhJ4kzf2KYGJLON7VsKGBvZadWLQZo,621
|
10
10
|
qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
|
11
11
|
qulab/executor/analyze.py,sha256=4Hau5LrKUdpweh7W94tcG4ahgxucHOevbM0hm57T7zE,5649
|
12
|
-
qulab/executor/cli.py,sha256=
|
12
|
+
qulab/executor/cli.py,sha256=SDZAuEJmRRt874N2paI78xuBk88twTfk-aynutCHNAk,18476
|
13
13
|
qulab/executor/load.py,sha256=eMlzOrrn8GpbP3J3uY5JJ8iO7tL5B1DWP1z2qiGyNhU,20385
|
14
14
|
qulab/executor/registry.py,sha256=gym9F5FIDY5eV-cSCZsP99wC4l-6jkx9VMjJMaPOLaQ,4730
|
15
15
|
qulab/executor/schedule.py,sha256=7gAJFwj13j1niGjVa1fSzwOS22eNFEN1hdrN3dfTY6A,21410
|
@@ -96,9 +96,9 @@ qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2ca
|
|
96
96
|
qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
|
97
97
|
qulab/visualization/rot3d.py,sha256=lMrEJlRLwYe6NMBlGkKYpp_V9CTipOAuDy6QW_cQK00,734
|
98
98
|
qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
|
99
|
-
qulab-2.11.
|
100
|
-
qulab-2.11.
|
101
|
-
qulab-2.11.
|
102
|
-
qulab-2.11.
|
103
|
-
qulab-2.11.
|
104
|
-
qulab-2.11.
|
99
|
+
qulab-2.11.4.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
|
100
|
+
qulab-2.11.4.dist-info/METADATA,sha256=qkJE05G3P9IwYbwoet89FssRyf_WtmEE8MJejsmKvOc,3896
|
101
|
+
qulab-2.11.4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
102
|
+
qulab-2.11.4.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
|
103
|
+
qulab-2.11.4.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
104
|
+
qulab-2.11.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|