hpcflow-new2 0.2.0a189__py3-none-any.whl → 0.2.0a199__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.
- hpcflow/__pyinstaller/hook-hpcflow.py +9 -6
- hpcflow/_version.py +1 -1
- hpcflow/app.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/environments.yaml +1 -1
- hpcflow/sdk/__init__.py +26 -15
- hpcflow/sdk/app.py +2192 -768
- hpcflow/sdk/cli.py +506 -296
- hpcflow/sdk/cli_common.py +105 -7
- hpcflow/sdk/config/__init__.py +1 -1
- hpcflow/sdk/config/callbacks.py +115 -43
- hpcflow/sdk/config/cli.py +126 -103
- hpcflow/sdk/config/config.py +674 -318
- hpcflow/sdk/config/config_file.py +131 -95
- hpcflow/sdk/config/errors.py +125 -84
- hpcflow/sdk/config/types.py +148 -0
- hpcflow/sdk/core/__init__.py +25 -1
- hpcflow/sdk/core/actions.py +1771 -1059
- hpcflow/sdk/core/app_aware.py +24 -0
- hpcflow/sdk/core/cache.py +139 -79
- hpcflow/sdk/core/command_files.py +263 -287
- hpcflow/sdk/core/commands.py +145 -112
- hpcflow/sdk/core/element.py +828 -535
- hpcflow/sdk/core/enums.py +192 -0
- hpcflow/sdk/core/environment.py +74 -93
- hpcflow/sdk/core/errors.py +455 -52
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +540 -272
- hpcflow/sdk/core/loop.py +751 -347
- hpcflow/sdk/core/loop_cache.py +164 -47
- hpcflow/sdk/core/object_list.py +370 -207
- hpcflow/sdk/core/parameters.py +1100 -627
- hpcflow/sdk/core/rule.py +59 -41
- hpcflow/sdk/core/run_dir_files.py +21 -37
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +1649 -1339
- hpcflow/sdk/core/task_schema.py +308 -196
- hpcflow/sdk/core/test_utils.py +191 -114
- hpcflow/sdk/core/types.py +440 -0
- hpcflow/sdk/core/utils.py +485 -309
- hpcflow/sdk/core/validation.py +82 -9
- hpcflow/sdk/core/workflow.py +2544 -1178
- hpcflow/sdk/core/zarr_io.py +98 -137
- hpcflow/sdk/data/workflow_spec_schema.yaml +2 -0
- hpcflow/sdk/demo/cli.py +53 -33
- hpcflow/sdk/helper/cli.py +18 -15
- hpcflow/sdk/helper/helper.py +75 -63
- hpcflow/sdk/helper/watcher.py +61 -28
- hpcflow/sdk/log.py +122 -71
- hpcflow/sdk/persistence/__init__.py +8 -31
- hpcflow/sdk/persistence/base.py +1360 -606
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +38 -0
- hpcflow/sdk/persistence/json.py +568 -188
- hpcflow/sdk/persistence/pending.py +382 -179
- hpcflow/sdk/persistence/store_resource.py +39 -23
- hpcflow/sdk/persistence/types.py +318 -0
- hpcflow/sdk/persistence/utils.py +14 -11
- hpcflow/sdk/persistence/zarr.py +1337 -433
- hpcflow/sdk/runtime.py +44 -41
- hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
- hpcflow/sdk/submission/jobscript.py +1651 -692
- hpcflow/sdk/submission/schedulers/__init__.py +167 -39
- hpcflow/sdk/submission/schedulers/direct.py +121 -81
- hpcflow/sdk/submission/schedulers/sge.py +170 -129
- hpcflow/sdk/submission/schedulers/slurm.py +291 -268
- hpcflow/sdk/submission/schedulers/utils.py +12 -2
- hpcflow/sdk/submission/shells/__init__.py +14 -15
- hpcflow/sdk/submission/shells/base.py +150 -29
- hpcflow/sdk/submission/shells/bash.py +283 -173
- hpcflow/sdk/submission/shells/os_version.py +31 -30
- hpcflow/sdk/submission/shells/powershell.py +228 -170
- hpcflow/sdk/submission/submission.py +1014 -335
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +182 -12
- hpcflow/sdk/utils/arrays.py +71 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +12 -0
- hpcflow/sdk/utils/strings.py +33 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +27 -6
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +34 -35
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +866 -85
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +12 -4
- hpcflow/tests/unit/test_action.py +262 -75
- hpcflow/tests/unit/test_action_rule.py +9 -4
- hpcflow/tests/unit/test_app.py +33 -6
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +134 -1
- hpcflow/tests/unit/test_command.py +71 -54
- hpcflow/tests/unit/test_config.py +142 -16
- hpcflow/tests/unit/test_config_file.py +21 -18
- hpcflow/tests/unit/test_element.py +58 -62
- hpcflow/tests/unit/test_element_iteration.py +50 -1
- hpcflow/tests/unit/test_element_set.py +29 -19
- hpcflow/tests/unit/test_group.py +4 -2
- hpcflow/tests/unit/test_input_source.py +116 -93
- hpcflow/tests/unit/test_input_value.py +29 -24
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +44 -35
- hpcflow/tests/unit/test_loop.py +1396 -84
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +229 -0
- hpcflow/tests/unit/test_object_list.py +17 -12
- hpcflow/tests/unit/test_parameter.py +29 -7
- hpcflow/tests/unit/test_persistence.py +237 -42
- hpcflow/tests/unit/test_resources.py +20 -18
- hpcflow/tests/unit/test_run.py +117 -6
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +2 -1
- hpcflow/tests/unit/test_schema_input.py +23 -15
- hpcflow/tests/unit/test_shell.py +23 -2
- hpcflow/tests/unit/test_slurm.py +8 -7
- hpcflow/tests/unit/test_submission.py +38 -89
- hpcflow/tests/unit/test_task.py +352 -247
- hpcflow/tests/unit/test_task_schema.py +33 -20
- hpcflow/tests/unit/test_utils.py +9 -11
- hpcflow/tests/unit/test_value_sequence.py +15 -12
- hpcflow/tests/unit/test_workflow.py +114 -83
- hpcflow/tests/unit/test_workflow_template.py +0 -1
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +334 -1
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +160 -15
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6587 -3
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/METADATA +8 -4
- hpcflow_new2-0.2.0a199.dist-info/RECORD +221 -0
- hpcflow/sdk/core/parallel.py +0 -21
- hpcflow_new2-0.2.0a189.dist-info/RECORD +0 -158
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/entry_points.txt +0 -0
hpcflow/sdk/cli.py
CHANGED
@@ -2,12 +2,15 @@
|
|
2
2
|
Command line interface implementation.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
6
|
+
import contextlib
|
5
7
|
import json
|
6
8
|
import os
|
7
|
-
|
9
|
+
import time
|
8
10
|
import click
|
9
11
|
from colorama import init as colorama_init
|
10
|
-
from termcolor import colored
|
12
|
+
from termcolor import colored # type: ignore
|
13
|
+
from typing import TYPE_CHECKING
|
11
14
|
from rich.pretty import pprint
|
12
15
|
|
13
16
|
from hpcflow import __version__, _app_name
|
@@ -31,7 +34,9 @@ from hpcflow.sdk.cli_common import (
|
|
31
34
|
tasks_opt,
|
32
35
|
cancel_opt,
|
33
36
|
submit_status_opt,
|
37
|
+
force_arr_opt,
|
34
38
|
make_status_opt,
|
39
|
+
add_sub_opt,
|
35
40
|
zip_path_opt,
|
36
41
|
zip_overwrite_opt,
|
37
42
|
zip_log_opt,
|
@@ -42,11 +47,27 @@ from hpcflow.sdk.cli_common import (
|
|
42
47
|
rechunk_backup_opt,
|
43
48
|
rechunk_chunk_size_opt,
|
44
49
|
rechunk_status_opt,
|
50
|
+
cancel_status_opt,
|
51
|
+
list_js_max_js_opt,
|
52
|
+
list_js_jobscripts_opt,
|
53
|
+
list_task_js_max_js_opt,
|
54
|
+
list_task_js_task_names_opt,
|
55
|
+
list_js_width_opt,
|
56
|
+
jobscript_std_array_idx_opt,
|
45
57
|
_add_doc_from_help,
|
46
58
|
)
|
47
59
|
from hpcflow.sdk.helper.cli import get_helper_CLI
|
48
60
|
from hpcflow.sdk.log import TimeIt
|
61
|
+
from hpcflow.sdk.core.workflow import Workflow
|
49
62
|
from hpcflow.sdk.submission.shells import ALL_SHELLS
|
63
|
+
from hpcflow.sdk.submission.jobscript import Jobscript
|
64
|
+
from hpcflow.sdk.submission.submission import Submission
|
65
|
+
from hpcflow.sdk.submission.schedulers.sge import SGEPosix
|
66
|
+
|
67
|
+
if TYPE_CHECKING:
|
68
|
+
from pathlib import Path
|
69
|
+
from typing import Literal
|
70
|
+
from .app import BaseApp
|
50
71
|
|
51
72
|
#: Standard option
|
52
73
|
string_option = click.option(
|
@@ -59,16 +80,61 @@ string_option = click.option(
|
|
59
80
|
workflow_ref_type_opt = click.option(
|
60
81
|
"--ref-type",
|
61
82
|
"-r",
|
62
|
-
type=click.Choice(
|
83
|
+
type=click.Choice(("assume-id", "id", "path")),
|
63
84
|
default="assume-id",
|
64
85
|
help="How to interpret a reference, as an ID, a path, or to guess.",
|
65
86
|
)
|
66
87
|
|
88
|
+
#: Get the current workflow from the context.
|
89
|
+
_pass_workflow = click.make_pass_decorator(Workflow)
|
90
|
+
#: Get the current submission from the context.
|
91
|
+
_pass_submission = click.make_pass_decorator(Submission)
|
92
|
+
#: Get the current jobscript from the context.
|
93
|
+
_pass_js = click.make_pass_decorator(Jobscript)
|
67
94
|
|
68
95
|
_add_doc_from_help(string_option, workflow_ref_type_opt)
|
69
96
|
|
70
97
|
|
71
|
-
|
98
|
+
class ErrorPropagatingClickContext(click.Context):
|
99
|
+
"""A click Context class that passes on exception information.
|
100
|
+
|
101
|
+
Using the standard `click.Context` class, exceptions raised when using a resource specified
|
102
|
+
with `ctx.with_resource(my_ctx_manager())` are not passed on to the `__exit__` method of
|
103
|
+
`my_ctx_manager`. See: https://github.com/pallets/click/issues/2447.
|
104
|
+
|
105
|
+
Examples
|
106
|
+
--------
|
107
|
+
>>> @click.group()
|
108
|
+
... @click.pass_context
|
109
|
+
... def cli(ctx):
|
110
|
+
... ctx.with_resource(my_context_manager())
|
111
|
+
... cli.context_class = ErrorPropagatingClickContext
|
112
|
+
|
113
|
+
"""
|
114
|
+
|
115
|
+
def __exit__(self, exc_type, exc_value, tb):
|
116
|
+
self._depth -= 1
|
117
|
+
if self._depth == 0:
|
118
|
+
self._exit_stack.__exit__(exc_type, exc_value, tb)
|
119
|
+
self._exit_stack = contextlib.ExitStack()
|
120
|
+
click.core.pop_context()
|
121
|
+
|
122
|
+
|
123
|
+
@contextlib.contextmanager
|
124
|
+
def redirect_std_to_file_click(file, mode: str = "a"):
|
125
|
+
def ignore(exc):
|
126
|
+
"""Do not intercept Click's `Exit` exception when the exit code is zero."""
|
127
|
+
if type(exc) is click.exceptions.Exit:
|
128
|
+
if exc.exit_code == 0:
|
129
|
+
return True
|
130
|
+
return exc.exit_code
|
131
|
+
return 1 # default exit code
|
132
|
+
|
133
|
+
with utils.redirect_std_to_file(file=file, ignore=ignore):
|
134
|
+
yield
|
135
|
+
|
136
|
+
|
137
|
+
def parse_jobscript_wait_spec(jobscripts: str) -> dict[int, list[int]]:
|
72
138
|
"""
|
73
139
|
Parse a jobscript wait specification.
|
74
140
|
"""
|
@@ -79,7 +145,15 @@ def parse_jobscript_wait_spec(jobscripts: str) -> Dict[int, List[int]]:
|
|
79
145
|
return sub_js_idx_dct
|
80
146
|
|
81
147
|
|
82
|
-
def
|
148
|
+
def _set_help_name(cmd: click.Group | click.Command, app: BaseApp):
|
149
|
+
"""
|
150
|
+
Update the help string of the command to contain the name of the application.
|
151
|
+
"""
|
152
|
+
if cmd.help:
|
153
|
+
cmd.help = cmd.help.format(app_name=app.name)
|
154
|
+
|
155
|
+
|
156
|
+
def _make_API_CLI(app: BaseApp):
|
83
157
|
"""Generate the CLI for the main functionality."""
|
84
158
|
|
85
159
|
@click.command(name="make")
|
@@ -94,18 +168,20 @@ def _make_API_CLI(app):
|
|
94
168
|
@ts_name_fmt_option
|
95
169
|
@variables_option
|
96
170
|
@make_status_opt
|
171
|
+
@add_sub_opt
|
97
172
|
def make_workflow(
|
98
|
-
template_file_or_str,
|
99
|
-
string,
|
100
|
-
format,
|
101
|
-
path,
|
102
|
-
name,
|
103
|
-
overwrite,
|
104
|
-
store,
|
105
|
-
ts_fmt=None,
|
106
|
-
ts_name_fmt=None,
|
107
|
-
variables=None,
|
108
|
-
status=True,
|
173
|
+
template_file_or_str: str,
|
174
|
+
string: bool,
|
175
|
+
format: Literal["json", "yaml"] | None,
|
176
|
+
path: Path | None,
|
177
|
+
name: str | None,
|
178
|
+
overwrite: bool,
|
179
|
+
store: str,
|
180
|
+
ts_fmt: str | None = None,
|
181
|
+
ts_name_fmt: str | None = None,
|
182
|
+
variables: list[tuple[str, str]] | None = None,
|
183
|
+
status: bool = True,
|
184
|
+
add_submission: bool = False,
|
109
185
|
):
|
110
186
|
"""Generate a new {app_name} workflow.
|
111
187
|
|
@@ -123,9 +199,11 @@ def _make_API_CLI(app):
|
|
123
199
|
store=store,
|
124
200
|
ts_fmt=ts_fmt,
|
125
201
|
ts_name_fmt=ts_name_fmt,
|
126
|
-
variables=dict(variables),
|
202
|
+
variables=dict(variables) if variables is not None else None,
|
127
203
|
status=status,
|
204
|
+
add_submission=add_submission,
|
128
205
|
)
|
206
|
+
assert isinstance(wk, Workflow)
|
129
207
|
click.echo(wk.path)
|
130
208
|
|
131
209
|
@click.command(name="go")
|
@@ -147,23 +225,23 @@ def _make_API_CLI(app):
|
|
147
225
|
@cancel_opt
|
148
226
|
@submit_status_opt
|
149
227
|
def make_and_submit_workflow(
|
150
|
-
template_file_or_str,
|
151
|
-
string,
|
152
|
-
format,
|
153
|
-
path,
|
154
|
-
name,
|
155
|
-
overwrite,
|
156
|
-
store,
|
157
|
-
ts_fmt=None,
|
158
|
-
ts_name_fmt=None,
|
159
|
-
variables=None,
|
160
|
-
js_parallelism=None,
|
161
|
-
wait=False,
|
162
|
-
add_to_known=True,
|
163
|
-
print_idx=False,
|
164
|
-
tasks=None,
|
165
|
-
cancel=False,
|
166
|
-
status=True,
|
228
|
+
template_file_or_str: str,
|
229
|
+
string: bool,
|
230
|
+
format: Literal["json", "yaml"] | None,
|
231
|
+
path: Path | None,
|
232
|
+
name: str | None,
|
233
|
+
overwrite: bool,
|
234
|
+
store: str,
|
235
|
+
ts_fmt: str | None = None,
|
236
|
+
ts_name_fmt: str | None = None,
|
237
|
+
variables: list[tuple[str, str]] | None = None,
|
238
|
+
js_parallelism: bool | None = None,
|
239
|
+
wait: bool = False,
|
240
|
+
add_to_known: bool = True,
|
241
|
+
print_idx: bool = False,
|
242
|
+
tasks: list[int] | None = None,
|
243
|
+
cancel: bool = False,
|
244
|
+
status: bool = True,
|
167
245
|
):
|
168
246
|
"""Generate and submit a new {app_name} workflow.
|
169
247
|
|
@@ -182,7 +260,7 @@ def _make_API_CLI(app):
|
|
182
260
|
store=store,
|
183
261
|
ts_fmt=ts_fmt,
|
184
262
|
ts_name_fmt=ts_name_fmt,
|
185
|
-
variables=dict(variables),
|
263
|
+
variables=dict(variables) if variables is not None else None,
|
186
264
|
JS_parallelism=js_parallelism,
|
187
265
|
wait=wait,
|
188
266
|
add_to_known=add_to_known,
|
@@ -192,12 +270,13 @@ def _make_API_CLI(app):
|
|
192
270
|
status=status,
|
193
271
|
)
|
194
272
|
if print_idx:
|
273
|
+
assert isinstance(out, tuple)
|
195
274
|
click.echo(out[1])
|
196
275
|
|
197
276
|
@click.command(context_settings={"ignore_unknown_options": True})
|
198
277
|
@click.argument("py_test_args", nargs=-1, type=click.UNPROCESSED)
|
199
278
|
@click.pass_context
|
200
|
-
def test(ctx, py_test_args):
|
279
|
+
def test(ctx: click.Context, py_test_args: list[str]):
|
201
280
|
"""Run {app_name} test suite.
|
202
281
|
|
203
282
|
PY_TEST_ARGS are arguments passed on to Pytest.
|
@@ -208,7 +287,7 @@ def _make_API_CLI(app):
|
|
208
287
|
@click.command(context_settings={"ignore_unknown_options": True})
|
209
288
|
@click.argument("py_test_args", nargs=-1, type=click.UNPROCESSED)
|
210
289
|
@click.pass_context
|
211
|
-
def test_hpcflow(ctx, py_test_args):
|
290
|
+
def test_hpcflow(ctx: click.Context, py_test_args: list[str]):
|
212
291
|
"""Run hpcFlow test suite.
|
213
292
|
|
214
293
|
PY_TEST_ARGS are arguments passed on to Pytest.
|
@@ -222,8 +301,7 @@ def _make_API_CLI(app):
|
|
222
301
|
test,
|
223
302
|
]
|
224
303
|
for cmd in commands:
|
225
|
-
|
226
|
-
cmd.help = cmd.help.format(app_name=app.name)
|
304
|
+
_set_help_name(cmd, app)
|
227
305
|
|
228
306
|
if app.name != "hpcFlow":
|
229
307
|
# `test_hpcflow` is the same as `test` for the hpcflow app no need to add both:
|
@@ -232,118 +310,173 @@ def _make_API_CLI(app):
|
|
232
310
|
return commands
|
233
311
|
|
234
312
|
|
235
|
-
def _make_workflow_submission_jobscript_CLI(app):
|
313
|
+
def _make_workflow_submission_jobscript_CLI(app: BaseApp):
|
236
314
|
"""Generate the CLI for interacting with existing workflow submission
|
237
315
|
jobscripts."""
|
238
316
|
|
239
317
|
@click.group(name="js")
|
318
|
+
@_pass_submission
|
240
319
|
@click.pass_context
|
241
320
|
@click.argument("js_idx", type=click.INT)
|
242
|
-
def jobscript(ctx, js_idx):
|
321
|
+
def jobscript(ctx: click.Context, sb: Submission, js_idx: int):
|
243
322
|
"""Interact with existing {app_name} workflow submission jobscripts.
|
244
323
|
|
245
324
|
JS_IDX is the jobscript index within the submission object.
|
246
325
|
|
247
326
|
"""
|
248
|
-
ctx.obj
|
327
|
+
ctx.obj = sb.jobscripts[js_idx]
|
249
328
|
|
250
329
|
@jobscript.command(name="res")
|
251
|
-
@
|
252
|
-
def resources(
|
330
|
+
@_pass_js
|
331
|
+
def resources(job: Jobscript):
|
253
332
|
"""Get resources associated with this jobscript."""
|
254
|
-
click.echo(
|
333
|
+
click.echo(job.resources.__dict__)
|
255
334
|
|
256
335
|
@jobscript.command(name="deps")
|
257
|
-
@
|
258
|
-
def dependencies(
|
336
|
+
@_pass_js
|
337
|
+
def dependencies(job: Jobscript):
|
259
338
|
"""Get jobscript dependencies."""
|
260
|
-
click.echo(
|
339
|
+
click.echo(job.dependencies)
|
261
340
|
|
262
341
|
@jobscript.command()
|
263
|
-
@
|
264
|
-
def path(
|
342
|
+
@_pass_js
|
343
|
+
def path(job: Jobscript):
|
265
344
|
"""Get the file path to the jobscript."""
|
266
|
-
click.echo(
|
345
|
+
click.echo(job.jobscript_path)
|
267
346
|
|
268
347
|
@jobscript.command()
|
269
|
-
@
|
270
|
-
def show(
|
348
|
+
@_pass_js
|
349
|
+
def show(job: Jobscript):
|
271
350
|
"""Show the jobscript file."""
|
272
|
-
with
|
351
|
+
with job.jobscript_path.open("rt") as fp:
|
273
352
|
click.echo(fp.read())
|
274
353
|
|
275
|
-
jobscript.
|
354
|
+
@jobscript.command()
|
355
|
+
@jobscript_std_array_idx_opt
|
356
|
+
@_pass_js
|
357
|
+
def stdout(job: Jobscript, array_idx: int):
|
358
|
+
"""Print the contents of the standard output stream file."""
|
359
|
+
job.print_stdout(array_idx=array_idx)
|
360
|
+
|
361
|
+
@jobscript.command()
|
362
|
+
@jobscript_std_array_idx_opt
|
363
|
+
@_pass_js
|
364
|
+
def stderr(job: Jobscript, array_idx: int):
|
365
|
+
"""Print the contents of the standard error stream file."""
|
366
|
+
job.print_stderr(array_idx=array_idx)
|
276
367
|
|
368
|
+
_set_help_name(jobscript, app)
|
277
369
|
return jobscript
|
278
370
|
|
279
371
|
|
280
|
-
def _make_workflow_submission_CLI(app):
|
372
|
+
def _make_workflow_submission_CLI(app: BaseApp):
|
281
373
|
"""Generate the CLI for interacting with existing workflow submissions."""
|
282
374
|
|
283
375
|
@click.group(name="sub")
|
376
|
+
@_pass_workflow
|
284
377
|
@click.pass_context
|
285
378
|
@click.argument("sub_idx", type=click.INT)
|
286
|
-
def submission(ctx, sub_idx):
|
379
|
+
def submission(ctx: click.Context, wf: Workflow, sub_idx: int):
|
287
380
|
"""Interact with existing {app_name} workflow submissions.
|
288
381
|
|
289
382
|
SUB_IDX is the submission index.
|
290
383
|
|
291
384
|
"""
|
292
|
-
ctx.obj
|
385
|
+
ctx.obj = wf.submissions[sub_idx]
|
293
386
|
|
294
387
|
@submission.command("status")
|
295
|
-
@
|
296
|
-
def status(
|
388
|
+
@_pass_submission
|
389
|
+
def status(sb: Submission):
|
297
390
|
"""Get the submission status."""
|
298
|
-
click.echo(
|
391
|
+
click.echo(sb.status.name.lower())
|
299
392
|
|
300
393
|
@submission.command("submitted-js")
|
301
|
-
@
|
302
|
-
def submitted_JS(
|
394
|
+
@_pass_submission
|
395
|
+
def submitted_JS(sb: Submission):
|
303
396
|
"""Get a list of jobscript indices that have been submitted."""
|
304
|
-
click.echo(
|
397
|
+
click.echo(sb.submitted_jobscripts)
|
305
398
|
|
306
399
|
@submission.command("outstanding-js")
|
307
|
-
@
|
308
|
-
def outstanding_JS(
|
400
|
+
@_pass_submission
|
401
|
+
def outstanding_JS(sb: Submission):
|
309
402
|
"""Get a list of jobscript indices that have not yet been submitted."""
|
310
|
-
click.echo(
|
403
|
+
click.echo(sb.outstanding_jobscripts)
|
311
404
|
|
312
405
|
@submission.command("needs-submit")
|
313
|
-
@
|
314
|
-
def needs_submit(
|
406
|
+
@_pass_submission
|
407
|
+
def needs_submit(sb: Submission):
|
315
408
|
"""Check if this submission needs submitting."""
|
316
|
-
click.echo(
|
409
|
+
click.echo(sb.needs_submit)
|
317
410
|
|
318
411
|
@submission.command("get-active-jobscripts")
|
319
|
-
@
|
320
|
-
def get_active_jobscripts(
|
412
|
+
@_pass_submission
|
413
|
+
def get_active_jobscripts(sb: Submission):
|
321
414
|
"""Show active jobscripts and their jobscript-element states."""
|
322
|
-
pprint(
|
415
|
+
pprint(sb.get_active_jobscripts(as_json=True))
|
323
416
|
|
324
|
-
submission.
|
325
|
-
|
417
|
+
@submission.command()
|
418
|
+
@_pass_submission
|
419
|
+
def get_scheduler_job_IDs(sb: Submission):
|
420
|
+
"""Print jobscript scheduler job IDs."""
|
421
|
+
job_IDs = sb.get_scheduler_job_IDs()
|
422
|
+
if job_IDs:
|
423
|
+
print("\n".join(job_IDs))
|
424
|
+
|
425
|
+
@submission.command()
|
426
|
+
@_pass_submission
|
427
|
+
def get_process_IDs(sb: Submission):
|
428
|
+
"""Print jobscript process IDs."""
|
429
|
+
proc_IDs = sb.get_process_IDs()
|
430
|
+
if proc_IDs:
|
431
|
+
print("\n".join(str(i) for i in proc_IDs))
|
432
|
+
|
433
|
+
@submission.command()
|
434
|
+
@list_js_max_js_opt
|
435
|
+
@list_js_jobscripts_opt
|
436
|
+
@list_js_width_opt
|
437
|
+
@_pass_submission
|
438
|
+
def list_jobscripts(
|
439
|
+
sb: Submission, max_js: int | None, jobscripts: str | None, width: int | None
|
440
|
+
):
|
441
|
+
"""Print a table listing jobscripts and associated information."""
|
442
|
+
jobscripts_ = [int(i) for i in jobscripts.split(",")] if jobscripts else None
|
443
|
+
sb.list_jobscripts(max_js=max_js, jobscripts=jobscripts_, width=width)
|
444
|
+
|
445
|
+
@submission.command()
|
446
|
+
@list_task_js_max_js_opt
|
447
|
+
@list_task_js_task_names_opt
|
448
|
+
@list_js_width_opt
|
449
|
+
@_pass_submission
|
450
|
+
def list_task_jobscripts(
|
451
|
+
sb: Submission,
|
452
|
+
max_js: int | None,
|
453
|
+
task_names: str | None,
|
454
|
+
width: int | None,
|
455
|
+
):
|
456
|
+
"""Print a table listing tasks and their associated jobscripts."""
|
457
|
+
task_names_ = list(task_names.split(",")) if task_names else None
|
458
|
+
sb.list_task_jobscripts(task_names=task_names_, max_js=max_js, width=width)
|
326
459
|
|
460
|
+
_set_help_name(submission, app)
|
461
|
+
submission.add_command(_make_workflow_submission_jobscript_CLI(app))
|
327
462
|
return submission
|
328
463
|
|
329
464
|
|
330
|
-
def _make_workflow_CLI(app):
|
465
|
+
def _make_workflow_CLI(app: BaseApp):
|
331
466
|
"""Generate the CLI for interacting with existing workflows."""
|
332
467
|
|
333
468
|
@click.group()
|
334
469
|
@click.argument("workflow_ref")
|
335
470
|
@workflow_ref_type_opt
|
336
471
|
@click.pass_context
|
337
|
-
def workflow(ctx, workflow_ref, ref_type):
|
472
|
+
def workflow(ctx: click.Context, workflow_ref: str, ref_type: str | None):
|
338
473
|
"""Interact with existing {app_name} workflows.
|
339
474
|
|
340
475
|
WORKFLOW_REF is the path to, or local ID of, an existing workflow.
|
341
476
|
|
342
477
|
"""
|
343
478
|
workflow_path = app._resolve_workflow_reference(workflow_ref, ref_type)
|
344
|
-
|
345
|
-
ctx.ensure_object(dict)
|
346
|
-
ctx.obj["workflow"] = wk
|
479
|
+
ctx.obj = app.Workflow(workflow_path)
|
347
480
|
|
348
481
|
@workflow.command(name="submit")
|
349
482
|
@js_parallelism_option
|
@@ -353,23 +486,23 @@ def _make_workflow_CLI(app):
|
|
353
486
|
@tasks_opt
|
354
487
|
@cancel_opt
|
355
488
|
@submit_status_opt
|
356
|
-
@
|
489
|
+
@_pass_workflow
|
357
490
|
def submit_workflow(
|
358
|
-
|
359
|
-
js_parallelism=None,
|
360
|
-
wait=False,
|
361
|
-
add_to_known=True,
|
362
|
-
print_idx=False,
|
363
|
-
tasks=None,
|
364
|
-
cancel=False,
|
365
|
-
status=True,
|
491
|
+
wf: Workflow,
|
492
|
+
js_parallelism: bool | None = None,
|
493
|
+
wait: bool = False,
|
494
|
+
add_to_known: bool = True,
|
495
|
+
print_idx: bool = False,
|
496
|
+
tasks: list[int] | None = None,
|
497
|
+
cancel: bool = False,
|
498
|
+
status: bool = True,
|
366
499
|
):
|
367
500
|
"""Submit the workflow."""
|
368
|
-
out =
|
501
|
+
out = wf.submit(
|
369
502
|
JS_parallelism=js_parallelism,
|
370
503
|
wait=wait,
|
371
504
|
add_to_known=add_to_known,
|
372
|
-
return_idx=
|
505
|
+
return_idx=True,
|
373
506
|
tasks=tasks,
|
374
507
|
cancel=cancel,
|
375
508
|
status=status,
|
@@ -377,6 +510,27 @@ def _make_workflow_CLI(app):
|
|
377
510
|
if print_idx:
|
378
511
|
click.echo(out)
|
379
512
|
|
513
|
+
@workflow.command(name="add-submission")
|
514
|
+
@js_parallelism_option
|
515
|
+
@tasks_opt
|
516
|
+
@force_arr_opt
|
517
|
+
@submit_status_opt
|
518
|
+
@click.pass_context
|
519
|
+
def add_submission(
|
520
|
+
ctx,
|
521
|
+
js_parallelism=None,
|
522
|
+
tasks=None,
|
523
|
+
force_array=False,
|
524
|
+
status=True,
|
525
|
+
):
|
526
|
+
"""Add a new submission to the workflow, but do not submit."""
|
527
|
+
ctx.obj["workflow"].add_submission(
|
528
|
+
JS_parallelism=js_parallelism,
|
529
|
+
tasks=tasks,
|
530
|
+
force_array=force_array,
|
531
|
+
status=status,
|
532
|
+
)
|
533
|
+
|
380
534
|
@workflow.command(name="wait")
|
381
535
|
@click.option(
|
382
536
|
"-j",
|
@@ -389,19 +543,19 @@ def _make_workflow_CLI(app):
|
|
389
543
|
"separate patterns like these."
|
390
544
|
),
|
391
545
|
)
|
392
|
-
@
|
393
|
-
def wait(
|
546
|
+
@_pass_workflow
|
547
|
+
def wait(wf: Workflow, jobscripts: str | None):
|
394
548
|
js_spec = parse_jobscript_wait_spec(jobscripts) if jobscripts else None
|
395
|
-
|
549
|
+
wf.wait(sub_js=js_spec)
|
396
550
|
|
397
551
|
@workflow.command(name="abort-run")
|
398
552
|
@click.option("--submission", type=click.INT, default=-1)
|
399
553
|
@click.option("--task", type=click.INT)
|
400
554
|
@click.option("--element", type=click.INT)
|
401
|
-
@
|
402
|
-
def abort_run(
|
555
|
+
@_pass_workflow
|
556
|
+
def abort_run(wf: Workflow, submission: int, task: int, element: int):
|
403
557
|
"""Abort the specified run."""
|
404
|
-
|
558
|
+
wf.abort_run(
|
405
559
|
submission_idx=submission,
|
406
560
|
task_idx=task,
|
407
561
|
element_idx=element,
|
@@ -409,36 +563,36 @@ def _make_workflow_CLI(app):
|
|
409
563
|
|
410
564
|
@workflow.command(name="get-param")
|
411
565
|
@click.argument("index", type=click.INT)
|
412
|
-
@
|
413
|
-
def get_parameter(
|
566
|
+
@_pass_workflow
|
567
|
+
def get_parameter(wf: Workflow, index: int):
|
414
568
|
"""Get a parameter value by data index."""
|
415
|
-
click.echo(
|
569
|
+
click.echo(wf.get_parameter_data(index))
|
416
570
|
|
417
571
|
@workflow.command(name="get-param-source")
|
418
572
|
@click.argument("index", type=click.INT)
|
419
|
-
@
|
420
|
-
def get_parameter_source(
|
573
|
+
@_pass_workflow
|
574
|
+
def get_parameter_source(wf: Workflow, index: int):
|
421
575
|
"""Get a parameter source by data index."""
|
422
|
-
click.echo(
|
576
|
+
click.echo(wf.get_parameter_source(index))
|
423
577
|
|
424
578
|
@workflow.command(name="get-all-params")
|
425
|
-
@
|
426
|
-
def get_all_parameters(
|
579
|
+
@_pass_workflow
|
580
|
+
def get_all_parameters(wf: Workflow):
|
427
581
|
"""Get all parameter values."""
|
428
|
-
click.echo(
|
582
|
+
click.echo(wf.get_all_parameter_data())
|
429
583
|
|
430
584
|
@workflow.command(name="is-param-set")
|
431
585
|
@click.argument("index", type=click.INT)
|
432
|
-
@
|
433
|
-
def is_parameter_set(
|
586
|
+
@_pass_workflow
|
587
|
+
def is_parameter_set(wf: Workflow, index: int):
|
434
588
|
"""Check if a parameter specified by data index is set."""
|
435
|
-
click.echo(
|
589
|
+
click.echo(wf.is_parameter_set(index))
|
436
590
|
|
437
591
|
@workflow.command(name="show-all-status")
|
438
|
-
@
|
439
|
-
def show_all_EAR_statuses(
|
592
|
+
@_pass_workflow
|
593
|
+
def show_all_EAR_statuses(wf: Workflow):
|
440
594
|
"""Show the submission status of all workflow EARs."""
|
441
|
-
|
595
|
+
wf.show_all_EAR_statuses()
|
442
596
|
|
443
597
|
@workflow.command(name="zip")
|
444
598
|
@zip_path_opt
|
@@ -446,19 +600,19 @@ def _make_workflow_CLI(app):
|
|
446
600
|
@zip_log_opt
|
447
601
|
@zip_include_execute_opt
|
448
602
|
@zip_include_rechunk_backups_opt
|
449
|
-
@
|
603
|
+
@_pass_workflow
|
450
604
|
def zip_workflow(
|
451
|
-
|
452
|
-
path,
|
453
|
-
overwrite,
|
454
|
-
log,
|
455
|
-
include_execute,
|
456
|
-
include_rechunk_backups,
|
605
|
+
wf: Workflow,
|
606
|
+
path: str,
|
607
|
+
overwrite: bool,
|
608
|
+
log: str | None,
|
609
|
+
include_execute: bool,
|
610
|
+
include_rechunk_backups: bool,
|
457
611
|
):
|
458
612
|
"""Generate a copy of the workflow in the zip file format in the current working
|
459
613
|
directory."""
|
460
614
|
click.echo(
|
461
|
-
|
615
|
+
wf.zip(
|
462
616
|
path=path,
|
463
617
|
overwrite=overwrite,
|
464
618
|
log=log,
|
@@ -470,54 +624,114 @@ def _make_workflow_CLI(app):
|
|
470
624
|
@workflow.command(name="unzip")
|
471
625
|
@unzip_path_opt
|
472
626
|
@unzip_log_opt
|
473
|
-
@
|
474
|
-
def unzip_workflow(
|
627
|
+
@_pass_workflow
|
628
|
+
def unzip_workflow(wf: Workflow, path: str, log: str | None):
|
475
629
|
"""Generate a copy of the zipped workflow in the submittable Zarr format in the
|
476
630
|
current working directory."""
|
477
|
-
click.echo(
|
631
|
+
click.echo(wf.unzip(path=path, log=log))
|
478
632
|
|
479
633
|
@workflow.command(name="rechunk")
|
480
634
|
@rechunk_backup_opt
|
481
635
|
@rechunk_chunk_size_opt
|
482
636
|
@rechunk_status_opt
|
483
|
-
@
|
484
|
-
def rechunk(
|
637
|
+
@_pass_workflow
|
638
|
+
def rechunk(wf: Workflow, backup: bool, chunk_size: int, status: bool):
|
485
639
|
"""Rechunk metadata/runs and parameters/base arrays."""
|
486
|
-
|
640
|
+
wf.rechunk(backup=backup, chunk_size=chunk_size, status=status)
|
487
641
|
|
488
642
|
@workflow.command(name="rechunk-runs")
|
489
643
|
@rechunk_backup_opt
|
490
644
|
@rechunk_chunk_size_opt
|
491
645
|
@rechunk_status_opt
|
492
|
-
@
|
493
|
-
def rechunk_runs(
|
646
|
+
@_pass_workflow
|
647
|
+
def rechunk_runs(wf: Workflow, backup: bool, chunk_size: int, status: bool):
|
494
648
|
"""Rechunk the metadata/runs array."""
|
495
|
-
|
496
|
-
backup=backup, chunk_size=chunk_size, status=status
|
497
|
-
)
|
649
|
+
wf.rechunk_runs(backup=backup, chunk_size=chunk_size, status=status)
|
498
650
|
|
499
651
|
@workflow.command(name="rechunk-parameter-base")
|
500
652
|
@rechunk_backup_opt
|
501
653
|
@rechunk_chunk_size_opt
|
502
654
|
@rechunk_status_opt
|
503
|
-
@
|
504
|
-
def rechunk_parameter_base(
|
655
|
+
@_pass_workflow
|
656
|
+
def rechunk_parameter_base(wf: Workflow, backup: bool, chunk_size: int, status: bool):
|
505
657
|
"""Rechunk the parameters/base array."""
|
506
|
-
|
507
|
-
|
658
|
+
wf.rechunk_parameter_base(backup=backup, chunk_size=chunk_size, status=status)
|
659
|
+
|
660
|
+
@workflow.command()
|
661
|
+
@_pass_workflow
|
662
|
+
def get_scheduler_job_IDs(wf: Workflow):
|
663
|
+
"""Print jobscript scheduler job IDs from all submissions of this workflow."""
|
664
|
+
job_IDs = wf.get_scheduler_job_IDs()
|
665
|
+
if job_IDs:
|
666
|
+
print("\n".join(job_IDs))
|
667
|
+
|
668
|
+
@workflow.command()
|
669
|
+
@_pass_workflow
|
670
|
+
def get_process_IDs(wf: Workflow):
|
671
|
+
"""Print jobscript process IDs from all submissions of this workflow."""
|
672
|
+
proc_IDs = wf.get_process_IDs()
|
673
|
+
if proc_IDs:
|
674
|
+
print("\n".join(str(i) for i in proc_IDs))
|
675
|
+
|
676
|
+
@workflow.command()
|
677
|
+
@click.option(
|
678
|
+
"--sub-idx",
|
679
|
+
type=click.INT,
|
680
|
+
default=0,
|
681
|
+
help="Submission index whose jobscripts are to be shown.",
|
682
|
+
)
|
683
|
+
@list_js_max_js_opt
|
684
|
+
@list_js_jobscripts_opt
|
685
|
+
@list_js_width_opt
|
686
|
+
@_pass_workflow
|
687
|
+
def list_jobscripts(
|
688
|
+
wf: Workflow,
|
689
|
+
sub_idx: int,
|
690
|
+
max_js: int | None,
|
691
|
+
jobscripts: str | None,
|
692
|
+
width: int | None,
|
693
|
+
):
|
694
|
+
"""Print a table listing jobscripts and associated information from the specified
|
695
|
+
submission."""
|
696
|
+
jobscripts_ = [int(i) for i in jobscripts.split(",")] if jobscripts else None
|
697
|
+
wf.list_jobscripts(
|
698
|
+
sub_idx=sub_idx, max_js=max_js, jobscripts=jobscripts_, width=width
|
508
699
|
)
|
509
700
|
|
510
|
-
workflow.
|
701
|
+
@workflow.command()
|
702
|
+
@click.option(
|
703
|
+
"--sub-idx",
|
704
|
+
type=click.INT,
|
705
|
+
default=0,
|
706
|
+
help="Submission index whose tasks are to be shown.",
|
707
|
+
)
|
708
|
+
@list_task_js_max_js_opt
|
709
|
+
@list_task_js_task_names_opt
|
710
|
+
@list_js_width_opt
|
711
|
+
@_pass_workflow
|
712
|
+
def list_task_jobscripts(
|
713
|
+
wf: Workflow,
|
714
|
+
sub_idx: int,
|
715
|
+
max_js: int | None,
|
716
|
+
task_names: str | None,
|
717
|
+
width: int | None,
|
718
|
+
):
|
719
|
+
"""Print a table listing tasks and their associated jobscripts from the specified
|
720
|
+
submission."""
|
721
|
+
task_names_ = list(task_names.split(",")) if task_names else None
|
722
|
+
wf.list_task_jobscripts(
|
723
|
+
sub_idx=sub_idx, task_names=task_names_, max_js=max_js, width=width
|
724
|
+
)
|
511
725
|
|
726
|
+
_set_help_name(workflow, app)
|
512
727
|
workflow.add_command(_make_workflow_submission_CLI(app))
|
513
|
-
|
514
728
|
return workflow
|
515
729
|
|
516
730
|
|
517
|
-
def _make_submission_CLI(app):
|
731
|
+
def _make_submission_CLI(app: BaseApp):
|
518
732
|
"""Generate the CLI for submission related queries."""
|
519
733
|
|
520
|
-
def OS_info_callback(ctx, param, value):
|
734
|
+
def OS_info_callback(ctx: click.Context, param, value: bool):
|
521
735
|
if not value or ctx.resilient_parsing:
|
522
736
|
return
|
523
737
|
pprint(app.get_OS_info())
|
@@ -532,16 +746,15 @@ def _make_submission_CLI(app):
|
|
532
746
|
expose_value=False,
|
533
747
|
callback=OS_info_callback,
|
534
748
|
)
|
535
|
-
|
536
|
-
def submission(ctx):
|
749
|
+
def submission():
|
537
750
|
"""Submission-related queries."""
|
538
|
-
|
751
|
+
pass
|
539
752
|
|
540
753
|
@submission.command("shell-info")
|
541
|
-
@click.argument("shell_name", type=click.Choice(ALL_SHELLS))
|
754
|
+
@click.argument("shell_name", type=click.Choice(list(ALL_SHELLS)))
|
542
755
|
@click.option("--exclude-os", is_flag=True, default=False)
|
543
756
|
@click.pass_context
|
544
|
-
def shell_info(ctx, shell_name, exclude_os):
|
757
|
+
def shell_info(ctx: click.Context, shell_name: str, exclude_os: bool):
|
545
758
|
"""Show information about the specified shell, such as the version."""
|
546
759
|
pprint(app.get_shell_info(shell_name, exclude_os))
|
547
760
|
ctx.exit()
|
@@ -549,13 +762,14 @@ def _make_submission_CLI(app):
|
|
549
762
|
@submission.group("scheduler")
|
550
763
|
@click.argument("scheduler_name")
|
551
764
|
@click.pass_context
|
552
|
-
def scheduler(ctx, scheduler_name):
|
553
|
-
ctx.obj
|
765
|
+
def scheduler(ctx: click.Context, scheduler_name: str):
|
766
|
+
ctx.obj = app.get_scheduler(scheduler_name, os.name)
|
767
|
+
|
768
|
+
pass_scheduler = click.make_pass_decorator(SGEPosix)
|
554
769
|
|
555
770
|
@scheduler.command()
|
556
|
-
@
|
557
|
-
def get_login_nodes(
|
558
|
-
scheduler = ctx.obj["scheduler_obj"]
|
771
|
+
@pass_scheduler
|
772
|
+
def get_login_nodes(scheduler: SGEPosix):
|
559
773
|
pprint(scheduler.get_login_nodes())
|
560
774
|
|
561
775
|
@submission.command()
|
@@ -566,8 +780,7 @@ def _make_submission_CLI(app):
|
|
566
780
|
default=False,
|
567
781
|
help="Do not format and only show JSON-compatible information.",
|
568
782
|
)
|
569
|
-
|
570
|
-
def get_known(ctx, as_json=False):
|
783
|
+
def get_known(as_json: bool = False):
|
571
784
|
"""Print known-submissions information as a formatted Python object."""
|
572
785
|
out = app.get_known_submissions(as_json=as_json)
|
573
786
|
if as_json:
|
@@ -578,11 +791,11 @@ def _make_submission_CLI(app):
|
|
578
791
|
return submission
|
579
792
|
|
580
793
|
|
581
|
-
def _make_internal_CLI(app):
|
794
|
+
def _make_internal_CLI(app: BaseApp):
|
582
795
|
"""Generate the CLI for internal use."""
|
583
796
|
|
584
797
|
@click.group()
|
585
|
-
def internal(help=True): # TEMP
|
798
|
+
def internal(help: bool = True): # TEMP
|
586
799
|
"""Internal CLI to be invoked by scripts generated by the app."""
|
587
800
|
pass
|
588
801
|
|
@@ -591,47 +804,75 @@ def _make_internal_CLI(app):
|
|
591
804
|
"""Get the invocation command for this app instance."""
|
592
805
|
click.echo(app.run_time_info.invocation_command)
|
593
806
|
|
807
|
+
@internal.command()
|
808
|
+
@click.pass_context
|
809
|
+
@click.option("--raise", "raise_opt", is_flag=True)
|
810
|
+
@click.option("--click-exit-code", type=click.INT)
|
811
|
+
@click.option("--sleep", type=click.INT)
|
812
|
+
def noop(ctx, raise_opt, click_exit_code, sleep):
|
813
|
+
"""Used only in CLI tests."""
|
814
|
+
if raise_opt:
|
815
|
+
raise ValueError("internal noop raised!")
|
816
|
+
elif click_exit_code is not None:
|
817
|
+
ctx.exit(click_exit_code)
|
818
|
+
elif sleep:
|
819
|
+
time.sleep(sleep)
|
820
|
+
|
594
821
|
@internal.group()
|
595
822
|
@click.argument("path", type=click.Path(exists=True))
|
596
823
|
@click.pass_context
|
597
|
-
def workflow(ctx, path):
|
824
|
+
def workflow(ctx: click.Context, path: Path):
|
598
825
|
""""""
|
599
|
-
|
600
|
-
ctx.ensure_object(dict)
|
601
|
-
ctx.obj["workflow"] = wk
|
826
|
+
ctx.obj = app.Workflow(path)
|
602
827
|
|
603
828
|
@workflow.command()
|
604
|
-
@
|
829
|
+
@_pass_workflow
|
605
830
|
@click.argument("submission_idx", type=click.INT)
|
606
831
|
@click.argument("jobscript_idx", type=click.INT)
|
607
|
-
@click.argument("
|
608
|
-
@click.argument("
|
609
|
-
|
610
|
-
|
832
|
+
@click.argument("block_idx", type=click.INT)
|
833
|
+
@click.argument("block_action_idx", type=click.INT)
|
834
|
+
@click.argument("run_id", type=click.INT)
|
835
|
+
def execute_run(
|
836
|
+
wf: Workflow,
|
611
837
|
submission_idx: int,
|
612
838
|
jobscript_idx: int,
|
613
|
-
|
614
|
-
|
839
|
+
block_idx: int,
|
840
|
+
block_action_idx: int,
|
841
|
+
run_id: int,
|
615
842
|
):
|
616
|
-
app.CLI_logger.info(f"
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
js_action_idx,
|
622
|
-
ear_id,
|
623
|
-
)
|
843
|
+
app.CLI_logger.info(f"execute commands for EAR ID {run_id!r}.")
|
844
|
+
wf.execute_run(
|
845
|
+
submission_idx=submission_idx,
|
846
|
+
block_act_key=(jobscript_idx, block_idx, block_action_idx),
|
847
|
+
run_ID=run_id,
|
624
848
|
)
|
625
849
|
|
626
850
|
@workflow.command()
|
627
|
-
@
|
851
|
+
@_pass_workflow
|
852
|
+
@click.argument("submission_idx", type=click.INT)
|
853
|
+
@click.argument("jobscript_idx", type=click.INT)
|
854
|
+
def execute_combined_runs(
|
855
|
+
wf: Workflow,
|
856
|
+
submission_idx: int,
|
857
|
+
jobscript_idx: int,
|
858
|
+
):
|
859
|
+
app.CLI_logger.info(
|
860
|
+
f"execute command for combined scripts of jobscript {jobscript_idx}."
|
861
|
+
)
|
862
|
+
wf.execute_combined_runs(
|
863
|
+
submission_idx=submission_idx,
|
864
|
+
jobscript_idx=jobscript_idx,
|
865
|
+
)
|
866
|
+
|
867
|
+
@workflow.command()
|
868
|
+
@_pass_workflow
|
628
869
|
@click.argument("name")
|
629
870
|
@click.argument("value")
|
630
871
|
@click.argument("ear_id", type=click.INT)
|
631
872
|
@click.argument("cmd_idx", type=click.INT)
|
632
873
|
@click.option("--stderr", is_flag=True, default=False)
|
633
874
|
def save_parameter(
|
634
|
-
|
875
|
+
wf: Workflow,
|
635
876
|
name: str,
|
636
877
|
value: str,
|
637
878
|
ear_id: int,
|
@@ -643,9 +884,8 @@ def _make_internal_CLI(app):
|
|
643
884
|
f"{cmd_idx!r} (stderr={stderr!r})"
|
644
885
|
)
|
645
886
|
app.CLI_logger.debug(f"save parameter value is: {value!r}")
|
646
|
-
|
647
|
-
|
648
|
-
value = wk.process_shell_parameter_output(
|
887
|
+
with wf._store.cached_load():
|
888
|
+
value = wf.process_shell_parameter_output(
|
649
889
|
name=name,
|
650
890
|
value=value,
|
651
891
|
EAR_ID=ear_id,
|
@@ -653,63 +893,7 @@ def _make_internal_CLI(app):
|
|
653
893
|
stderr=stderr,
|
654
894
|
)
|
655
895
|
app.CLI_logger.debug(f"save parameter processed value is: {value!r}")
|
656
|
-
|
657
|
-
|
658
|
-
@workflow.command()
|
659
|
-
@click.pass_context
|
660
|
-
@click.argument("ear_id", type=click.INT)
|
661
|
-
def set_EAR_start(ctx, ear_id: int):
|
662
|
-
app.CLI_logger.info(f"set EAR start for EAR ID {ear_id!r}.")
|
663
|
-
ctx.exit(ctx.obj["workflow"].set_EAR_start(ear_id))
|
664
|
-
|
665
|
-
@workflow.command()
|
666
|
-
@click.pass_context
|
667
|
-
@click.argument("js_idx", type=click.INT)
|
668
|
-
@click.argument("js_act_idx", type=click.INT)
|
669
|
-
@click.argument("ear_id", type=click.INT)
|
670
|
-
@click.argument("exit_code", type=click.INT)
|
671
|
-
def set_EAR_end(
|
672
|
-
ctx,
|
673
|
-
js_idx: int,
|
674
|
-
js_act_idx: int,
|
675
|
-
ear_id: int,
|
676
|
-
exit_code: int,
|
677
|
-
):
|
678
|
-
app.CLI_logger.info(
|
679
|
-
f"set EAR end for EAR ID {ear_id!r} with exit code {exit_code!r}."
|
680
|
-
)
|
681
|
-
ctx.exit(
|
682
|
-
ctx.obj["workflow"].set_EAR_end(
|
683
|
-
js_idx=js_idx,
|
684
|
-
js_act_idx=js_act_idx,
|
685
|
-
EAR_ID=ear_id,
|
686
|
-
exit_code=exit_code,
|
687
|
-
)
|
688
|
-
)
|
689
|
-
|
690
|
-
@workflow.command()
|
691
|
-
@click.pass_context
|
692
|
-
@click.argument("ear_id", type=click.INT)
|
693
|
-
def set_EAR_skip(ctx, ear_id: int):
|
694
|
-
app.CLI_logger.info(f"set EAR skip for EAR ID {ear_id!r}.")
|
695
|
-
ctx.exit(ctx.obj["workflow"].set_EAR_skip(ear_id))
|
696
|
-
|
697
|
-
@workflow.command()
|
698
|
-
@click.pass_context
|
699
|
-
@click.argument("ear_id", type=click.INT)
|
700
|
-
def get_EAR_skipped(ctx, ear_id: int):
|
701
|
-
"""Return 1 if the given EAR is to be skipped, else return 0."""
|
702
|
-
app.CLI_logger.info(f"get EAR skip for EAR ID {ear_id!r}.")
|
703
|
-
click.echo(int(ctx.obj["workflow"].get_EAR_skipped(ear_id)))
|
704
|
-
|
705
|
-
@workflow.command()
|
706
|
-
@click.pass_context
|
707
|
-
@click.argument("loop_name", type=click.STRING)
|
708
|
-
@click.argument("ear_id", type=click.INT)
|
709
|
-
def check_loop(ctx, loop_name: str, ear_id: int):
|
710
|
-
"""Check if an iteration has met its loop's termination condition."""
|
711
|
-
app.CLI_logger.info(f"check_loop for loop {loop_name!r} and EAR ID {ear_id!r}.")
|
712
|
-
ctx.exit(ctx.obj["workflow"].check_loop_termination(loop_name, ear_id))
|
896
|
+
wf.save_parameter(name=name, value=value, EAR_ID=ear_id)
|
713
897
|
|
714
898
|
# TODO: in general, maybe the workflow command group can expose the simple Workflow
|
715
899
|
# properties; maybe use a decorator on the Workflow property object to signify
|
@@ -718,17 +902,17 @@ def _make_internal_CLI(app):
|
|
718
902
|
return internal
|
719
903
|
|
720
904
|
|
721
|
-
def _make_template_components_CLI(app):
|
905
|
+
def _make_template_components_CLI(app: BaseApp):
|
722
906
|
@click.command()
|
723
|
-
def tc(help=True):
|
907
|
+
def tc(help: bool = True):
|
724
908
|
"""For showing template component data."""
|
725
909
|
pprint(app.template_components)
|
726
910
|
|
727
911
|
return tc
|
728
912
|
|
729
913
|
|
730
|
-
def _make_show_CLI(app):
|
731
|
-
def show_legend_callback(ctx, param, value):
|
914
|
+
def _make_show_CLI(app: BaseApp):
|
915
|
+
def show_legend_callback(ctx: click.Context, param, value: bool):
|
732
916
|
if not value or ctx.resilient_parsing:
|
733
917
|
return
|
734
918
|
app.show_legend()
|
@@ -765,14 +949,14 @@ def _make_show_CLI(app):
|
|
765
949
|
expose_value=False,
|
766
950
|
callback=show_legend_callback,
|
767
951
|
)
|
768
|
-
def show(max_recent, full, no_update):
|
952
|
+
def show(max_recent: int, full: bool, no_update: bool):
|
769
953
|
"""Show information about running and recently active workflows."""
|
770
954
|
app.show(max_recent=max_recent, full=full, no_update=no_update)
|
771
955
|
|
772
956
|
return show
|
773
957
|
|
774
958
|
|
775
|
-
def _make_zip_CLI(app):
|
959
|
+
def _make_zip_CLI(app: BaseApp):
|
776
960
|
@click.command(name="zip")
|
777
961
|
@click.argument("workflow_ref")
|
778
962
|
@zip_path_opt
|
@@ -782,13 +966,13 @@ def _make_zip_CLI(app):
|
|
782
966
|
@zip_include_rechunk_backups_opt
|
783
967
|
@workflow_ref_type_opt
|
784
968
|
def zip_workflow(
|
785
|
-
workflow_ref,
|
786
|
-
path,
|
787
|
-
overwrite,
|
788
|
-
log,
|
789
|
-
include_execute,
|
790
|
-
include_rechunk_backups,
|
791
|
-
ref_type,
|
969
|
+
workflow_ref: str,
|
970
|
+
path: str,
|
971
|
+
overwrite: bool,
|
972
|
+
log: str | None,
|
973
|
+
include_execute: bool,
|
974
|
+
include_rechunk_backups: bool,
|
975
|
+
ref_type: str | None,
|
792
976
|
):
|
793
977
|
"""Generate a copy of the specified workflow in the zip file format in the
|
794
978
|
current working directory.
|
@@ -811,12 +995,12 @@ def _make_zip_CLI(app):
|
|
811
995
|
return zip_workflow
|
812
996
|
|
813
997
|
|
814
|
-
def _make_unzip_CLI(app):
|
998
|
+
def _make_unzip_CLI(app: BaseApp):
|
815
999
|
@click.command(name="unzip")
|
816
1000
|
@click.argument("workflow_path")
|
817
1001
|
@unzip_path_opt
|
818
1002
|
@unzip_log_opt
|
819
|
-
def unzip_workflow(workflow_path, path, log):
|
1003
|
+
def unzip_workflow(workflow_path: str, path: str, log: str | None):
|
820
1004
|
"""Generate a copy of the specified zipped workflow in the submittable Zarr
|
821
1005
|
format in the current working directory.
|
822
1006
|
|
@@ -829,30 +1013,37 @@ def _make_unzip_CLI(app):
|
|
829
1013
|
return unzip_workflow
|
830
1014
|
|
831
1015
|
|
832
|
-
def _make_cancel_CLI(app):
|
1016
|
+
def _make_cancel_CLI(app: BaseApp):
|
833
1017
|
@click.command()
|
834
1018
|
@click.argument("workflow_ref")
|
835
1019
|
@workflow_ref_type_opt
|
836
|
-
|
1020
|
+
@cancel_status_opt
|
1021
|
+
def cancel(workflow_ref: str, ref_type: str | None, status: bool):
|
837
1022
|
"""Stop all running jobscripts of the specified workflow.
|
838
1023
|
|
839
1024
|
WORKFLOW_REF is the local ID (that provided by the `show` command}) or the
|
840
1025
|
workflow path.
|
841
1026
|
|
842
1027
|
"""
|
843
|
-
app.cancel(workflow_ref, ref_type)
|
1028
|
+
app.cancel(workflow_ref=workflow_ref, ref_is_path=ref_type, status=status)
|
844
1029
|
|
845
1030
|
return cancel
|
846
1031
|
|
847
1032
|
|
848
|
-
def _make_rechunk_CLI(app):
|
1033
|
+
def _make_rechunk_CLI(app: BaseApp):
|
849
1034
|
@click.command(name="rechunk")
|
850
1035
|
@click.argument("workflow_ref")
|
851
1036
|
@workflow_ref_type_opt
|
852
1037
|
@rechunk_backup_opt
|
853
1038
|
@rechunk_chunk_size_opt
|
854
1039
|
@rechunk_status_opt
|
855
|
-
def rechunk(
|
1040
|
+
def rechunk(
|
1041
|
+
workflow_ref: str,
|
1042
|
+
ref_type: str | None,
|
1043
|
+
backup: bool,
|
1044
|
+
chunk_size: int,
|
1045
|
+
status: bool,
|
1046
|
+
):
|
856
1047
|
"""Rechunk metadata/runs and parameters/base arrays.
|
857
1048
|
|
858
1049
|
WORKFLOW_REF is the local ID (that provided by the `show` command}) or the
|
@@ -866,7 +1057,7 @@ def _make_rechunk_CLI(app):
|
|
866
1057
|
return rechunk
|
867
1058
|
|
868
1059
|
|
869
|
-
def _make_open_CLI(app):
|
1060
|
+
def _make_open_CLI(app: BaseApp):
|
870
1061
|
@click.group(name="open")
|
871
1062
|
def open_file():
|
872
1063
|
"""Open a file (for example {app_name}'s log file) using the default
|
@@ -874,9 +1065,9 @@ def _make_open_CLI(app):
|
|
874
1065
|
|
875
1066
|
@open_file.command()
|
876
1067
|
@click.option("--path", is_flag=True, default=False)
|
877
|
-
def log(path=False):
|
1068
|
+
def log(path: bool = False):
|
878
1069
|
"""Open the {app_name} log file."""
|
879
|
-
file_path = app.config.
|
1070
|
+
file_path = app.config.log_file_path
|
880
1071
|
if path:
|
881
1072
|
click.echo(file_path)
|
882
1073
|
else:
|
@@ -884,9 +1075,9 @@ def _make_open_CLI(app):
|
|
884
1075
|
|
885
1076
|
@open_file.command()
|
886
1077
|
@click.option("--path", is_flag=True, default=False)
|
887
|
-
def config(path=False):
|
1078
|
+
def config(path: bool = False):
|
888
1079
|
"""Open the {app_name} config file, or retrieve it's path."""
|
889
|
-
file_path = app.config.
|
1080
|
+
file_path = app.config.config_file_path
|
890
1081
|
if path:
|
891
1082
|
click.echo(file_path)
|
892
1083
|
else:
|
@@ -895,34 +1086,30 @@ def _make_open_CLI(app):
|
|
895
1086
|
@open_file.command()
|
896
1087
|
@click.option("--name")
|
897
1088
|
@click.option("--path", is_flag=True, default=False)
|
898
|
-
def env_source(name=None, path=False):
|
1089
|
+
def env_source(name: str | None = None, path: bool = False):
|
899
1090
|
"""Open a named environment sources file, or the first one."""
|
900
|
-
sources
|
901
|
-
if not sources:
|
1091
|
+
if not (sources := app.config.environment_sources):
|
902
1092
|
raise ValueError("No environment sources specified in the config file.")
|
903
|
-
file_paths = []
|
904
1093
|
if not name:
|
905
1094
|
file_paths = [sources[0]]
|
906
1095
|
else:
|
907
|
-
for
|
908
|
-
if i.name == name:
|
909
|
-
file_paths.append(i)
|
1096
|
+
file_paths = [pth for pth in sources if pth.name == name]
|
910
1097
|
if not file_paths:
|
911
1098
|
raise ValueError(
|
912
1099
|
f"No environment source named {name!r} could be found; available "
|
913
|
-
f"environment source files have names: {[
|
1100
|
+
f"environment source files have names: {[pth.name for pth in sources]!r}"
|
914
1101
|
)
|
915
1102
|
|
916
1103
|
assert len(file_paths) < 5 # don't open a stupid number of files
|
917
|
-
for
|
1104
|
+
for pth in file_paths:
|
918
1105
|
if path:
|
919
|
-
click.echo(
|
1106
|
+
click.echo(pth)
|
920
1107
|
else:
|
921
|
-
utils.open_file(
|
1108
|
+
utils.open_file(pth)
|
922
1109
|
|
923
1110
|
@open_file.command()
|
924
1111
|
@click.option("--path", is_flag=True, default=False)
|
925
|
-
def known_subs(path=False):
|
1112
|
+
def known_subs(path: bool = False):
|
926
1113
|
"""Open the known-submissions text file."""
|
927
1114
|
file_path = app.known_subs_file_path
|
928
1115
|
if path:
|
@@ -934,7 +1121,7 @@ def _make_open_CLI(app):
|
|
934
1121
|
@click.argument("workflow_ref")
|
935
1122
|
@click.option("--path", is_flag=True, default=False)
|
936
1123
|
@workflow_ref_type_opt
|
937
|
-
def workflow(workflow_ref, ref_type, path=False):
|
1124
|
+
def workflow(workflow_ref: str, ref_type: str | None, path: bool = False):
|
938
1125
|
"""Open a workflow directory using, for example, File Explorer on Windows."""
|
939
1126
|
workflow_path = app._resolve_workflow_reference(workflow_ref, ref_type)
|
940
1127
|
if path:
|
@@ -944,7 +1131,7 @@ def _make_open_CLI(app):
|
|
944
1131
|
|
945
1132
|
@open_file.command()
|
946
1133
|
@click.option("--path", is_flag=True, default=False)
|
947
|
-
def user_data_dir(path=False):
|
1134
|
+
def user_data_dir(path: bool = False):
|
948
1135
|
dir_path = app._ensure_user_data_dir()
|
949
1136
|
if path:
|
950
1137
|
click.echo(dir_path)
|
@@ -953,7 +1140,7 @@ def _make_open_CLI(app):
|
|
953
1140
|
|
954
1141
|
@open_file.command()
|
955
1142
|
@click.option("--path", is_flag=True, default=False)
|
956
|
-
def user_cache_dir(path=False):
|
1143
|
+
def user_cache_dir(path: bool = False):
|
957
1144
|
dir_path = app._ensure_user_cache_dir()
|
958
1145
|
if path:
|
959
1146
|
click.echo(dir_path)
|
@@ -962,7 +1149,7 @@ def _make_open_CLI(app):
|
|
962
1149
|
|
963
1150
|
@open_file.command()
|
964
1151
|
@click.option("--path", is_flag=True, default=False)
|
965
|
-
def user_runtime_dir(path=False):
|
1152
|
+
def user_runtime_dir(path: bool = False):
|
966
1153
|
dir_path = app._ensure_user_runtime_dir()
|
967
1154
|
if path:
|
968
1155
|
click.echo(dir_path)
|
@@ -971,7 +1158,7 @@ def _make_open_CLI(app):
|
|
971
1158
|
|
972
1159
|
@open_file.command()
|
973
1160
|
@click.option("--path", is_flag=True, default=False)
|
974
|
-
def user_data_hostname_dir(path=False):
|
1161
|
+
def user_data_hostname_dir(path: bool = False):
|
975
1162
|
dir_path = app._ensure_user_data_hostname_dir()
|
976
1163
|
if path:
|
977
1164
|
click.echo(dir_path)
|
@@ -980,7 +1167,7 @@ def _make_open_CLI(app):
|
|
980
1167
|
|
981
1168
|
@open_file.command()
|
982
1169
|
@click.option("--path", is_flag=True, default=False)
|
983
|
-
def user_cache_hostname_dir(path=False):
|
1170
|
+
def user_cache_hostname_dir(path: bool = False):
|
984
1171
|
dir_path = app._ensure_user_cache_hostname_dir()
|
985
1172
|
if path:
|
986
1173
|
click.echo(dir_path)
|
@@ -989,32 +1176,31 @@ def _make_open_CLI(app):
|
|
989
1176
|
|
990
1177
|
@open_file.command()
|
991
1178
|
@click.option("--path", is_flag=True, default=False)
|
992
|
-
def demo_data_cache_dir(path=False):
|
1179
|
+
def demo_data_cache_dir(path: bool = False):
|
993
1180
|
dir_path = app._ensure_demo_data_cache_dir()
|
994
1181
|
if path:
|
995
1182
|
click.echo(dir_path)
|
996
1183
|
else:
|
997
1184
|
utils.open_file(dir_path)
|
998
1185
|
|
999
|
-
open_file
|
1000
|
-
log
|
1001
|
-
config
|
1002
|
-
|
1186
|
+
_set_help_name(open_file, app)
|
1187
|
+
_set_help_name(log, app)
|
1188
|
+
_set_help_name(config, app)
|
1003
1189
|
return open_file
|
1004
1190
|
|
1005
1191
|
|
1006
|
-
def _make_demo_data_CLI(app):
|
1192
|
+
def _make_demo_data_CLI(app: BaseApp):
|
1007
1193
|
"""Generate the CLI for interacting with example data files that are used in demo
|
1008
1194
|
workflows."""
|
1009
1195
|
|
1010
|
-
def list_callback(ctx, param, value):
|
1196
|
+
def list_callback(ctx: click.Context, param, value: bool):
|
1011
1197
|
if not value or ctx.resilient_parsing:
|
1012
1198
|
return
|
1013
1199
|
# TODO: format with Rich with a one-line description
|
1014
1200
|
click.echo("\n".join(app.list_demo_data_files()))
|
1015
1201
|
ctx.exit()
|
1016
1202
|
|
1017
|
-
def cache_all_callback(ctx, param, value):
|
1203
|
+
def cache_all_callback(ctx: click.Context, param, value: bool):
|
1018
1204
|
if not value or ctx.resilient_parsing:
|
1019
1205
|
return
|
1020
1206
|
app.cache_all_demo_data_files()
|
@@ -1036,7 +1222,7 @@ def _make_demo_data_CLI(app):
|
|
1036
1222
|
@demo_data.command("copy")
|
1037
1223
|
@click.argument("file_name")
|
1038
1224
|
@click.argument("destination")
|
1039
|
-
def copy_demo_data(file_name, destination):
|
1225
|
+
def copy_demo_data(file_name: str, destination: str):
|
1040
1226
|
"""Copy a demo data file to the specified location."""
|
1041
1227
|
app.copy_demo_data(file_name=file_name, dst=destination)
|
1042
1228
|
|
@@ -1050,14 +1236,14 @@ def _make_demo_data_CLI(app):
|
|
1050
1236
|
callback=cache_all_callback,
|
1051
1237
|
)
|
1052
1238
|
@click.argument("file_name")
|
1053
|
-
def cache_demo_data(file_name):
|
1239
|
+
def cache_demo_data(file_name: str):
|
1054
1240
|
"""Ensure a demo data file is in the demo data cache."""
|
1055
1241
|
app.cache_demo_data_file(file_name)
|
1056
1242
|
|
1057
1243
|
return demo_data
|
1058
1244
|
|
1059
1245
|
|
1060
|
-
def _make_manage_CLI(app):
|
1246
|
+
def _make_manage_CLI(app: BaseApp):
|
1061
1247
|
"""Generate the CLI for infrequent app management tasks."""
|
1062
1248
|
|
1063
1249
|
@click.group()
|
@@ -1074,7 +1260,7 @@ def _make_manage_CLI(app):
|
|
1074
1260
|
"--config-dir",
|
1075
1261
|
help="The directory containing the config file to be reset.",
|
1076
1262
|
)
|
1077
|
-
def reset_config(config_dir):
|
1263
|
+
def reset_config(config_dir: str):
|
1078
1264
|
"""Reset the configuration file to defaults.
|
1079
1265
|
|
1080
1266
|
This can be used if the current configuration file is invalid."""
|
@@ -1085,7 +1271,7 @@ def _make_manage_CLI(app):
|
|
1085
1271
|
"--config-dir",
|
1086
1272
|
help="The directory containing the config file whose path is to be returned.",
|
1087
1273
|
)
|
1088
|
-
def get_config_path(config_dir):
|
1274
|
+
def get_config_path(config_dir: str):
|
1089
1275
|
"""Print the config file path without loading the config.
|
1090
1276
|
|
1091
1277
|
This can be used instead of `{app_name} open config --path` if the config file
|
@@ -1106,7 +1292,7 @@ def _make_manage_CLI(app):
|
|
1106
1292
|
|
1107
1293
|
@manage.command("clear-cache")
|
1108
1294
|
@click.option("--hostname", is_flag=True, default=False)
|
1109
|
-
def clear_cache(hostname):
|
1295
|
+
def clear_cache(hostname: bool):
|
1110
1296
|
"""Delete the app cache directory."""
|
1111
1297
|
if hostname:
|
1112
1298
|
app.clear_user_cache_hostname_dir()
|
@@ -1121,12 +1307,12 @@ def _make_manage_CLI(app):
|
|
1121
1307
|
return manage
|
1122
1308
|
|
1123
1309
|
|
1124
|
-
def make_cli(app):
|
1310
|
+
def make_cli(app: BaseApp):
|
1125
1311
|
"""Generate the root CLI for the app."""
|
1126
1312
|
|
1127
1313
|
colorama_init(autoreset=True)
|
1128
1314
|
|
1129
|
-
def run_time_info_callback(ctx, param, value):
|
1315
|
+
def run_time_info_callback(ctx: click.Context, param, value: bool):
|
1130
1316
|
app.run_time_info.from_CLI = True
|
1131
1317
|
if not value or ctx.resilient_parsing:
|
1132
1318
|
return
|
@@ -1180,8 +1366,25 @@ def make_cli(app):
|
|
1180
1366
|
"`TimeIt.decorator` are included."
|
1181
1367
|
),
|
1182
1368
|
)
|
1369
|
+
@click.option(
|
1370
|
+
"--std-stream",
|
1371
|
+
help="File to redirect standard output and error to, and to print exceptions to.",
|
1372
|
+
)
|
1183
1373
|
@click.pass_context
|
1184
|
-
def new_CLI(
|
1374
|
+
def new_CLI(
|
1375
|
+
ctx: click.Context,
|
1376
|
+
config_dir,
|
1377
|
+
config_key,
|
1378
|
+
with_config,
|
1379
|
+
timeit,
|
1380
|
+
timeit_file,
|
1381
|
+
std_stream: str,
|
1382
|
+
):
|
1383
|
+
ctx.ensure_object(dict)
|
1384
|
+
|
1385
|
+
if std_stream:
|
1386
|
+
ctx.with_resource(redirect_std_to_file_click(std_stream))
|
1387
|
+
|
1185
1388
|
app.run_time_info.from_CLI = True
|
1186
1389
|
TimeIt.active = timeit or timeit_file
|
1187
1390
|
TimeIt.file_path = timeit_file
|
@@ -1208,7 +1411,12 @@ def make_cli(app):
|
|
1208
1411
|
@click.option("--use-current-env", is_flag=True, default=False)
|
1209
1412
|
@click.option("--setup", type=click.STRING)
|
1210
1413
|
@click.option("--env-source-file", type=click.STRING)
|
1211
|
-
def configure_env(
|
1414
|
+
def configure_env(
|
1415
|
+
name: str,
|
1416
|
+
use_current_env: bool,
|
1417
|
+
setup: list[str] | None = None,
|
1418
|
+
env_source_file: str | None = None,
|
1419
|
+
):
|
1212
1420
|
"""Configure an app environment, using, for example, the currently activated
|
1213
1421
|
Python environment."""
|
1214
1422
|
app.configure_env(
|
@@ -1216,9 +1424,11 @@ def make_cli(app):
|
|
1216
1424
|
setup=setup,
|
1217
1425
|
executables=None,
|
1218
1426
|
use_current_env=use_current_env,
|
1219
|
-
env_source_file=env_source_file,
|
1427
|
+
env_source_file=None if env_source_file is None else Path(env_source_file),
|
1220
1428
|
)
|
1221
1429
|
|
1430
|
+
new_CLI.context_class = ErrorPropagatingClickContext
|
1431
|
+
|
1222
1432
|
new_CLI.__doc__ = app.description
|
1223
1433
|
new_CLI.add_command(get_config_CLI(app))
|
1224
1434
|
new_CLI.add_command(get_demo_software_CLI(app))
|