QuLab 2.4.14__cp312-cp312-macosx_10_13_universal2.whl → 2.4.15__cp312-cp312-macosx_10_13_universal2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: QuLab
3
- Version: 2.4.14
3
+ Version: 2.4.15
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,11 +1,13 @@
1
1
  qulab/__init__.py,sha256=P-Mx2p4TVmL91SoxoeXcj8Qm0x4xUf5Q_FLk0Yc_gIQ,138
2
- qulab/__main__.py,sha256=ThCRooT22_qpwFtbAvVOyHzpBCuGlfDVkXBXvApzgps,597
2
+ qulab/__main__.py,sha256=JgErYkiskih8Y6oRwowALtR-rwQhAAdqOYWjQraRIPI,59
3
3
  qulab/dicttree.py,sha256=tRRMpGZYVOLw0TEByE3_2Ss8FdOmzuGL9e1DWbs8qoY,13684
4
- qulab/fun.cpython-312-darwin.so,sha256=ctibPGPaVocV_T4x-lgvOvlubXeO0qoV-Wd5KL_gYpE,126864
5
- qulab/version.py,sha256=IPQCIOITfbX8l3dhIiuTYy89V7yOtf70dflfUCn6Dgk,22
4
+ qulab/fun.cpython-312-darwin.so,sha256=8DW2ynuxi-4mYbVw3P8eK95GTj4buG6ZunQOLDiqb_w,126864
5
+ qulab/version.py,sha256=QW2MBz1JeuFtoyMh-qAewXiVGQGFzZAiBx69V3wIX5U,22
6
+ qulab/cli/__init__.py,sha256=tgDIkkeIoasQXAifJZ6NU8jDgpNgb2a-B0C4nF0evrE,559
7
+ qulab/cli/config.py,sha256=QksTYD3-RBYBG8xnKLAh7EXrNOwxr128XXr30ZTFW6A,2983
6
8
  qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
7
- qulab/executor/__main__.py,sha256=qgZcdmx1wBZInHf6ionpuuZytIJnaDje9WRv-a8o2OM,5870
8
- qulab/executor/load.py,sha256=WNpJmHAxXaQKPkmycsqbqeuAuiudNi-4GscmBKfdKlw,9713
9
+ qulab/executor/cli.py,sha256=LRSb5-J8Tn7dj-fxwCp3OP_6YUgPRoY-f8O18sbWG8I,5164
10
+ qulab/executor/load.py,sha256=4qQrw9O5dmWYYwxWt15z7uc6B3YuRB7xKgKi0g8-YtA,10418
9
11
  qulab/executor/schedule.py,sha256=SfMZys8WdmIvfW5fPM42g-F6XwewTdpC0-puhf5TA54,11721
10
12
  qulab/executor/storage.py,sha256=M66Q5_Uc5MMfc_QAuuaaexwAz7wxBPMkeleB5nRpQmI,4621
11
13
  qulab/executor/transform.py,sha256=inaOn6eqCs22ZZ0xAQl8s8YCoEACaXSwFNNu7jqdwAk,2148
@@ -89,9 +91,9 @@ qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2ca
89
91
  qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
90
92
  qulab/visualization/rot3d.py,sha256=lMrEJlRLwYe6NMBlGkKYpp_V9CTipOAuDy6QW_cQK00,734
91
93
  qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
92
- QuLab-2.4.14.dist-info/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
93
- QuLab-2.4.14.dist-info/METADATA,sha256=TThc_IP0lzmpuuWgCsvPExFvPQ7a-to9Zscte5sjtq8,3699
94
- QuLab-2.4.14.dist-info/WHEEL,sha256=iDXcyuxg-66TzzqHGH-kgw_HJdaJE_1RHznrvPNCSNs,115
95
- QuLab-2.4.14.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
96
- QuLab-2.4.14.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
97
- QuLab-2.4.14.dist-info/RECORD,,
94
+ QuLab-2.4.15.dist-info/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
95
+ QuLab-2.4.15.dist-info/METADATA,sha256=EgZbZvkhzL5UTZFxqJ2yHVir2RSvAkmSCNd_nUgitdE,3699
96
+ QuLab-2.4.15.dist-info/WHEEL,sha256=iDXcyuxg-66TzzqHGH-kgw_HJdaJE_1RHznrvPNCSNs,115
97
+ QuLab-2.4.15.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
98
+ QuLab-2.4.15.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
99
+ QuLab-2.4.15.dist-info/RECORD,,
qulab/__main__.py CHANGED
@@ -1,32 +1,4 @@
1
- import click
2
-
3
- from .executor.__main__ import create, get, maintain, run, set
4
- from .monitor.__main__ import main as monitor
5
- from .scan.server import server
6
- from .sys.net.cli import dht
7
- from .visualization.__main__ import plot
8
-
9
-
10
- @click.group()
11
- def cli():
12
- pass
13
-
14
-
15
- @cli.command()
16
- def hello():
17
- """Print hello world."""
18
- click.echo('hello, world')
19
-
20
-
21
- cli.add_command(monitor)
22
- cli.add_command(plot)
23
- cli.add_command(dht)
24
- cli.add_command(server)
25
- cli.add_command(maintain)
26
- cli.add_command(run)
27
- cli.add_command(create)
28
- cli.add_command(set)
29
- cli.add_command(get)
1
+ from .cli import cli
30
2
 
31
3
  if __name__ == '__main__':
32
4
  cli()
qulab/cli/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ import click
2
+
3
+ from ..executor.cli import create, get, maintain, run, set
4
+ from ..monitor.__main__ import main as monitor
5
+ from ..scan.server import server
6
+ from ..sys.net.cli import dht
7
+ from ..visualization.__main__ import plot
8
+
9
+
10
+ @click.group()
11
+ def cli():
12
+ pass
13
+
14
+
15
+ @cli.command()
16
+ def hello():
17
+ """Print hello world."""
18
+ click.echo('hello, world')
19
+
20
+
21
+ cli.add_command(monitor)
22
+ cli.add_command(plot)
23
+ cli.add_command(dht)
24
+ cli.add_command(server)
25
+ cli.add_command(maintain)
26
+ cli.add_command(run)
27
+ cli.add_command(create)
28
+ cli.add_command(set)
29
+ cli.add_command(get)
qulab/cli/config.py ADDED
@@ -0,0 +1,88 @@
1
+ import configparser
2
+ import functools
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import click
8
+ from loguru import logger
9
+
10
+ CONFIG_PATH = os.path.expanduser("~/.qulab.ini")
11
+ ENV_PREFIX = "QULAB_"
12
+
13
+
14
+ def _get_config_value(option_name, type_cast=str, command_name=None):
15
+ """支持命令专属配置的优先级获取"""
16
+ # 构造环境变量名
17
+ if command_name:
18
+ env_var = f"{ENV_PREFIX}{command_name.upper()}_{option_name.upper()}"
19
+ config_section = command_name
20
+ else:
21
+ env_var = f"{ENV_PREFIX}{option_name.upper()}"
22
+ config_section = "common"
23
+
24
+ # 1. 检查环境变量
25
+ if env_value := os.getenv(env_var):
26
+ if type_cast is bool:
27
+ return env_value.lower() in ("true", "1", "yes")
28
+ if "path" in option_name:
29
+ return os.path.expanduser(env_value)
30
+ return type_cast(env_value)
31
+
32
+ # 2. 检查配置文件
33
+ config = configparser.ConfigParser()
34
+ # 先加载默认配置防止段不存在
35
+ config.read_dict({config_section: {}})
36
+ if Path(CONFIG_PATH).exists():
37
+ config.read(CONFIG_PATH)
38
+
39
+ # 从对应配置段读取
40
+ if config.has_section(config_section):
41
+ if config_value := config.get(config_section,
42
+ option_name,
43
+ fallback=None):
44
+ if type_cast is bool:
45
+ return config_value.lower() in ("true", "1", "yes")
46
+ if "path" in option_name:
47
+ return os.path.expanduser(config_value)
48
+ return type_cast(config_value)
49
+
50
+ return None # 交给 Click 处理默认值
51
+
52
+
53
+ def get_config_value(option_name, type_cast=str, command_name=None):
54
+ value = _get_config_value(option_name, type_cast, command_name)
55
+ if value is None and command_name is not None:
56
+ return _get_config_value(option_name, type_cast)
57
+ return value
58
+
59
+
60
+ def log_options(func):
61
+ """通用配置装饰器(所有命令共用)"""
62
+
63
+ @click.option("--debug",
64
+ is_flag=True,
65
+ default=lambda: get_config_value("debug", bool) or False,
66
+ help=f"Enable debug mode")
67
+ @click.option("--log",
68
+ type=click.Path(),
69
+ default=lambda: get_config_value("log"),
70
+ help=f"Log file path")
71
+ @functools.wraps(func)
72
+ def wrapper(*args, log=None, debug=False, **kwargs):
73
+ if log is None and not debug:
74
+ logger.remove()
75
+ logger.add(sys.stderr, level='INFO')
76
+ elif log is None and debug:
77
+ logger.remove()
78
+ logger.add(sys.stderr, level='DEBUG')
79
+ elif log is not None and not debug:
80
+ logger.configure(handlers=[dict(sink=log, level='INFO')])
81
+ elif log is not None and debug:
82
+ logger.configure(handlers=[
83
+ dict(sink=log, level='DEBUG'),
84
+ dict(sink=sys.stderr, level='DEBUG')
85
+ ])
86
+ return func(*args, **kwargs)
87
+
88
+ return wrapper
qulab/executor/cli.py ADDED
@@ -0,0 +1,178 @@
1
+ import functools
2
+ import importlib
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import click
8
+ from loguru import logger
9
+
10
+ from ..cli.config import ENV_PREFIX, get_config_value, log_options
11
+ from .load import find_unreferenced_workflows, get_entries, load_workflow
12
+ from .schedule import maintain as maintain_workflow
13
+ from .schedule import run as run_workflow
14
+ from .transform import set_config_api
15
+ from .utils import workflow_template
16
+
17
+
18
+ def command_option(command_name):
19
+ """命令专属配置装饰器工厂"""
20
+
21
+ def decorator(func):
22
+
23
+ @click.option(
24
+ '--code',
25
+ '-c',
26
+ default=lambda: get_config_value("code", str, command_name),
27
+ help='The path of the code.')
28
+ @click.option(
29
+ '--data',
30
+ '-d',
31
+ default=lambda: get_config_value("data", str, command_name),
32
+ help='The path of the data.')
33
+ @click.option(
34
+ '--api',
35
+ '-a',
36
+ default=lambda: get_config_value("api", str, command_name),
37
+ help='The modlule name of the api.')
38
+ @functools.wraps(func)
39
+ def wrapper(*args, **kwargs):
40
+ return func(*args, **kwargs)
41
+
42
+ return wrapper
43
+
44
+ return decorator
45
+
46
+
47
+ @click.command()
48
+ @click.argument('workflow')
49
+ @click.option('--code',
50
+ '-c',
51
+ default=lambda: get_config_value("code", str, 'create'),
52
+ help='The path of the code.')
53
+ def create(workflow, code):
54
+ """
55
+ Create a new workflow file.
56
+ """
57
+ if code is None:
58
+ code = Path.cwd()
59
+
60
+ fname = Path(code) / f'{workflow}'
61
+ fname = Path(os.path.expanduser(fname))
62
+ if fname.exists():
63
+ click.echo(f'{workflow} already exists')
64
+ return
65
+
66
+ fname.parent.mkdir(parents=True, exist_ok=True)
67
+ deps = find_unreferenced_workflows(code)
68
+
69
+ with open(fname, 'w') as f:
70
+ f.write(workflow_template(list(deps)))
71
+ click.echo(f'{workflow} created')
72
+
73
+
74
+ @click.command()
75
+ @click.argument('key')
76
+ @click.argument('value', type=str)
77
+ @click.option('--api',
78
+ '-a',
79
+ default=lambda: get_config_value("api", str, 'set'),
80
+ help='The modlule name of the api.')
81
+ def set(key, value, api):
82
+ """
83
+ Set a config.
84
+ """
85
+ from . import transform
86
+ if api is not None:
87
+ api = importlib.import_module(api)
88
+ set_config_api(api.query_config, api.update_config)
89
+ try:
90
+ value = eval(value)
91
+ except:
92
+ pass
93
+ transform.update_config({key: value})
94
+
95
+
96
+ @click.command()
97
+ @click.argument('key')
98
+ @click.option('--api',
99
+ '-a',
100
+ default=lambda: get_config_value("api", str, 'get'),
101
+ help='The modlule name of the api.')
102
+ def get(key, api):
103
+ """
104
+ Get a config.
105
+ """
106
+ from . import transform
107
+ if api is not None:
108
+ api = importlib.import_module(api)
109
+ set_config_api(api.query_config, api.update_config)
110
+ click.echo(transform.query_config(key))
111
+
112
+
113
+ @click.command()
114
+ @click.argument('workflow')
115
+ @click.option('--plot', '-p', is_flag=True, help='Plot the result.')
116
+ @click.option('--no-dependents',
117
+ '-n',
118
+ is_flag=True,
119
+ help='Do not run dependents.')
120
+ @log_options
121
+ @command_option('run')
122
+ def run(workflow, code, data, api, plot, no_dependents):
123
+ """
124
+ Run a workflow.
125
+ """
126
+ if api is not None:
127
+ api = importlib.import_module(api)
128
+ set_config_api(api.query_config, api.update_config)
129
+ if code is None:
130
+ code = Path.cwd()
131
+ if data is None:
132
+ data = Path(code) / 'logs'
133
+
134
+ code = Path(os.path.expanduser(code))
135
+ data = Path(os.path.expanduser(data))
136
+
137
+ wf = load_workflow(workflow, code)
138
+
139
+ if no_dependents:
140
+ if hasattr(wf, 'entries'):
141
+ for entry in get_entries(wf, code):
142
+ run_workflow(entry, code, data, plot=plot)
143
+ else:
144
+ run_workflow(wf, code, data, plot=plot)
145
+ else:
146
+ if hasattr(wf, 'entries'):
147
+ for entry in get_entries(wf, code):
148
+ maintain_workflow(entry, code, data, run=True, plot=plot)
149
+ else:
150
+ maintain_workflow(wf, code, data, run=True, plot=plot)
151
+
152
+
153
+ @click.command()
154
+ @click.argument('workflow')
155
+ @click.option('--plot', '-p', is_flag=True, help='Plot the result.')
156
+ @log_options
157
+ @command_option('maintain')
158
+ def maintain(workflow, code, data, api, plot):
159
+ """
160
+ Maintain a workflow.
161
+ """
162
+ if api is not None:
163
+ api = importlib.import_module(api)
164
+ set_config_api(api.query_config, api.update_config)
165
+ if code is None:
166
+ code = Path.cwd()
167
+ if data is None:
168
+ data = Path(code) / 'logs'
169
+
170
+ code = Path(os.path.expanduser(code))
171
+ data = Path(os.path.expanduser(data))
172
+
173
+ wf = load_workflow(workflow, code)
174
+ if hasattr(wf, 'entries'):
175
+ for entry in get_entries(wf, code):
176
+ maintain_workflow(entry, code, data, run=False, plot=plot)
177
+ else:
178
+ maintain_workflow(wf, code, data, run=False, plot=plot)
qulab/executor/load.py CHANGED
@@ -119,6 +119,17 @@ def is_workflow(module: ModuleType) -> bool:
119
119
  return False
120
120
 
121
121
 
122
+ def is_template(path: str | Path) -> bool:
123
+ path = Path(path)
124
+ if path.name == 'template.py':
125
+ return True
126
+ if path.name.endswith('_template.py'):
127
+ return True
128
+ if 'templates' in path.parts:
129
+ return True
130
+ return False
131
+
132
+
122
133
  def find_unreferenced_workflows(path: str) -> list[str]:
123
134
  root = Path(path).resolve()
124
135
  workflows = []
@@ -128,8 +139,7 @@ def find_unreferenced_workflows(path: str) -> list[str]:
128
139
  for file_path in root.rglob("*.py"):
129
140
  if file_path.name == "__init__.py":
130
141
  continue
131
- if file_path.name == 'template.py' or file_path.name.endswith(
132
- "_template.py"):
142
+ if is_template(file_path):
133
143
  continue
134
144
  try:
135
145
  rel_path = file_path.relative_to(root)
@@ -188,8 +198,6 @@ def find_unreferenced_workflows(path: str) -> list[str]:
188
198
  def load_workflow_from_file(file_name: str,
189
199
  base_path: str | Path,
190
200
  package='workflows') -> WorkflowType:
191
- if file_name.startswith('cfg:'):
192
- return SetConfigWorkflow(file_name[4:])
193
201
  base_path = Path(base_path)
194
202
  path = Path(file_name)
195
203
  module_name = f"{package}.{'.'.join([*path.parts[:-1], path.stem])}"
@@ -197,6 +205,9 @@ def load_workflow_from_file(file_name: str,
197
205
  module = module_from_spec(spec)
198
206
  spec.loader.exec_module(module)
199
207
 
208
+ if hasattr(module, 'entries'):
209
+ return module
210
+
200
211
  if not hasattr(module, '__timeout__'):
201
212
  module.__timeout__ = None
202
213
 
@@ -209,13 +220,13 @@ def load_workflow_from_file(file_name: str,
209
220
  return module
210
221
 
211
222
 
212
- def load_workflow_from_template(file_name: str,
223
+ def load_workflow_from_template(template_path: str,
213
224
  mappping: dict[str, str],
214
225
  base_path: str | Path,
215
- subtitle: str | None = None,
226
+ target_path: str | None = None,
216
227
  package='workflows') -> WorkflowType:
217
228
  base_path = Path(base_path)
218
- path = Path(file_name)
229
+ path = Path(template_path)
219
230
 
220
231
  with open(base_path / path) as f:
221
232
  content = f.read()
@@ -239,23 +250,31 @@ def load_workflow_from_template(file_name: str,
239
250
  content = template.substitute(mappping)
240
251
 
241
252
  hash_str = hashlib.md5(pickle.dumps(mappping)).hexdigest()[:8]
242
- if subtitle is None:
253
+ if target_path is None:
243
254
  if path.stem == 'template':
244
255
  path = path.parent / f'tmp{hash_str}.py'
245
- else:
256
+ elif path.stem.endswith('_template'):
246
257
  path = path.parent / path.stem.replace('_template',
247
258
  f'_tmp{hash_str}.py')
248
- else:
249
- if path.stem == 'template':
250
- path = path.parent / f'{subtitle}.py'
251
259
  else:
252
- path = path.parent / path.stem.replace('_template',
253
- f'_{subtitle}.py')
260
+ path = path.parent / f'{path.stem}_tmp{hash_str}.py'
261
+ else:
262
+ path = target_path
254
263
 
255
264
  file = base_path / path
256
265
  if not file.exists():
266
+ file.parent.mkdir(parents=True, exist_ok=True)
257
267
  with open(file, 'w') as f:
258
268
  f.write(content)
269
+ else:
270
+ if file.stat().st_mtime < Path(template_path).stat().st_mtime:
271
+ with open(file, 'w') as f:
272
+ f.write(content)
273
+ else:
274
+ if file.read_text() != content:
275
+ logger.warning(
276
+ f"`{file}` already exists and is different from the new one generated from template `{template_path}`"
277
+ )
259
278
 
260
279
  module = load_workflow_from_file(str(path), base_path, package)
261
280
 
@@ -271,9 +290,9 @@ def load_workflow(workflow: str | tuple[str, dict],
271
290
  w = load_workflow_from_template(file_name, mapping, base_path,
272
291
  None, package)
273
292
  elif len(workflow) == 3:
274
- file_name, subtitle, mapping = workflow
275
- w = load_workflow_from_template(file_name, mapping, base_path,
276
- subtitle, package)
293
+ template_path, target_path, mapping = workflow
294
+ w = load_workflow_from_template(template_path, mapping, base_path,
295
+ target_path, package)
277
296
  else:
278
297
  raise ValueError(f"Invalid workflow: {workflow}")
279
298
  w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
@@ -294,3 +313,7 @@ def load_workflow(workflow: str | tuple[str, dict],
294
313
  def get_dependents(workflow: WorkflowType,
295
314
  code_path: str | Path) -> list[WorkflowType]:
296
315
  return [load_workflow(n, code_path) for n in workflow.depends()[0]]
316
+
317
+
318
+ def get_entries(workflow: WorkflowType, code_path: str | Path) -> WorkflowType:
319
+ return [load_workflow(n, code_path) for n in workflow.entries()]
Binary file
qulab/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.4.14"
1
+ __version__ = "2.4.15"
@@ -1,200 +0,0 @@
1
- import functools
2
- import importlib
3
- import os
4
- import sys
5
- from pathlib import Path
6
-
7
- import click
8
- from loguru import logger
9
-
10
- from .load import find_unreferenced_workflows, load_workflow
11
- from .schedule import maintain as maintain_workflow
12
- from .schedule import run as run_workflow
13
- from .transform import set_config_api
14
- from .utils import workflow_template
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
-
42
- def log_options(func):
43
-
44
- @click.option("--debug", is_flag=True, help="Enable debug mode.")
45
- @click.option("--log", type=str, help="Log file path.")
46
- @functools.wraps(func) # 保持函数元信息
47
- def wrapper(*args, log=None, debug=False, **kwargs):
48
- if log is None and not debug:
49
- logger.remove()
50
- logger.add(sys.stderr, level='INFO')
51
- elif log is None and debug:
52
- logger.remove()
53
- logger.add(sys.stderr, level='DEBUG')
54
- elif log is not None and not debug:
55
- logger.configure(handlers=[dict(sink=log, level='INFO')])
56
- elif log is not None and debug:
57
- logger.configure(handlers=[
58
- dict(sink=log, level='DEBUG'),
59
- dict(sink=sys.stderr, level='DEBUG')
60
- ])
61
- return func(*args, **kwargs)
62
-
63
- return wrapper
64
-
65
-
66
- @click.group()
67
- def cli():
68
- pass
69
-
70
-
71
- @click.command()
72
- @click.argument('workflow')
73
- @click.option('--code', '-c', default=None, help='The path of the code.')
74
- def create(workflow, code):
75
- """
76
- Create a new workflow file.
77
- """
78
- if code is None:
79
- code = Path.cwd()
80
-
81
- fname = Path(code) / f'{workflow}'
82
- fname = Path(os.path.expanduser(fname))
83
- if fname.exists():
84
- click.echo(f'{workflow} already exists')
85
- return
86
-
87
- fname.parent.mkdir(parents=True, exist_ok=True)
88
- deps = find_unreferenced_workflows(code)
89
-
90
- with open(fname, 'w') as f:
91
- f.write(workflow_template(list(deps)))
92
- click.echo(f'{workflow} created')
93
-
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
-
128
- @click.command()
129
- @click.argument('workflow')
130
- @click.option('--code', '-c', default=None, help='The path of the code.')
131
- @click.option('--data', '-d', default=None, help='The path of the data.')
132
- @click.option('--api', '-a', default=None, help='The modlule name of the api.')
133
- @click.option('--plot', '-p', is_flag=True, help='Plot the result.')
134
- @click.option('--no-dependents',
135
- '-n',
136
- is_flag=True,
137
- help='Do not run dependents.')
138
- @log_options
139
- def run(workflow, code, data, api, plot, no_dependents):
140
- """
141
- Run a workflow.
142
- """
143
- if api is not None:
144
- api = importlib.import_module(api)
145
- set_config_api(api.query_config, api.update_config)
146
- if code is None:
147
- code = Path.cwd()
148
- if data is None:
149
- data = Path(code) / 'logs'
150
-
151
- code = Path(os.path.expanduser(code))
152
- data = Path(os.path.expanduser(data))
153
-
154
- if no_dependents:
155
- run_workflow(load_workflow(workflow, code), code, data, plot=plot)
156
- else:
157
- maintain_workflow(load_workflow(workflow, code),
158
- code,
159
- data,
160
- run=True,
161
- plot=plot)
162
-
163
-
164
- @click.command()
165
- @click.argument('workflow')
166
- @click.option('--code', '-c', default=None, help='The path of the code.')
167
- @click.option('--data', '-d', default=None, help='The path of the data.')
168
- @click.option('--api', '-a', default=None, help='The modlule name of the api.')
169
- @click.option('--plot', '-p', is_flag=True, help='Plot the result.')
170
- @log_options
171
- def maintain(workflow, code, data, api, plot):
172
- """
173
- Maintain a workflow.
174
- """
175
- if api is not None:
176
- api = importlib.import_module(api)
177
- set_config_api(api.query_config, api.update_config)
178
- if code is None:
179
- code = Path.cwd()
180
- if data is None:
181
- data = Path(code) / 'logs'
182
-
183
- code = Path(os.path.expanduser(code))
184
- data = Path(os.path.expanduser(data))
185
-
186
- maintain_workflow(load_workflow(workflow, code),
187
- code,
188
- data,
189
- run=False,
190
- plot=plot)
191
-
192
-
193
- cli.add_command(maintain)
194
- cli.add_command(run)
195
- cli.add_command(create)
196
- cli.add_command(set)
197
- cli.add_command(get)
198
-
199
- if __name__ == '__main__':
200
- cli()
File without changes