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/core/zarr_io.py
CHANGED
@@ -2,82 +2,71 @@
|
|
2
2
|
Utilities for working with Zarr.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from
|
5
|
+
from __future__ import annotations
|
6
|
+
from typing import Any
|
7
|
+
from typing_extensions import Self
|
6
8
|
|
7
|
-
import zarr
|
9
|
+
import zarr # type: ignore
|
8
10
|
import numpy as np
|
9
11
|
|
10
12
|
from hpcflow.sdk.core.utils import get_in_container, get_relative_path, set_in_container
|
11
13
|
|
12
14
|
|
13
|
-
|
15
|
+
#: The basic types that Zarr can handle directly with no special action.
|
16
|
+
PRIMITIVES: tuple[type, ...] = (
|
14
17
|
int,
|
15
18
|
float,
|
16
19
|
str,
|
17
20
|
type(None),
|
18
21
|
)
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
path = path or []
|
23
|
-
encoded = encoded or []
|
24
|
-
|
25
|
-
if len(path) > 50:
|
26
|
-
raise RuntimeError("I'm in too deep!")
|
27
|
-
|
28
|
-
if isinstance(obj, ZarrEncodable):
|
29
|
-
obj = obj.to_dict()
|
30
|
-
out, encoded = _zarr_encode(
|
31
|
-
obj, zarr_group=zarr_group, path=path, encoded=encoded
|
32
|
-
)
|
33
|
-
|
34
|
-
elif isinstance(obj, (list, tuple, set)):
|
35
|
-
out = []
|
36
|
-
for idx, item in enumerate(obj):
|
37
|
-
item, encoded = _zarr_encode(item, zarr_group, path + [idx], encoded)
|
38
|
-
out.append(item)
|
39
|
-
if isinstance(obj, tuple):
|
40
|
-
out = tuple(out)
|
41
|
-
elif isinstance(obj, set):
|
42
|
-
out = set(out)
|
43
|
-
|
44
|
-
elif isinstance(obj, dict):
|
45
|
-
out = {}
|
46
|
-
for dct_key, dct_val in obj.items():
|
47
|
-
dct_val, encoded = _zarr_encode(
|
48
|
-
dct_val, zarr_group, path + [dct_key], encoded
|
49
|
-
)
|
50
|
-
out.update({dct_key: dct_val})
|
51
|
-
|
52
|
-
elif isinstance(obj, PRIMITIVES):
|
53
|
-
out = obj
|
54
|
-
|
55
|
-
elif isinstance(obj, np.ndarray):
|
56
|
-
names = [int(i) for i in zarr_group.keys()]
|
57
|
-
if not names:
|
58
|
-
new_name = "0"
|
59
|
-
else:
|
60
|
-
new_name = str(max(names) + 1)
|
61
|
-
|
62
|
-
zarr_group.create_dataset(name=new_name, data=obj)
|
63
|
-
encoded.append(
|
64
|
-
{
|
65
|
-
"path": path,
|
66
|
-
"dataset": new_name,
|
67
|
-
}
|
68
|
-
)
|
69
|
-
out = None
|
70
|
-
|
71
|
-
return out, encoded
|
23
|
+
#: Maximum nesting depth for encoding.
|
24
|
+
MAX_DEPTH = 50
|
72
25
|
|
73
26
|
|
74
|
-
def zarr_encode(data, zarr_group, is_pending_add, is_set):
|
27
|
+
def zarr_encode(data, zarr_group: zarr.Group, is_pending_add: bool, is_set: bool):
|
75
28
|
"""
|
76
29
|
Encode data into a zarr group.
|
77
30
|
"""
|
78
|
-
|
31
|
+
|
32
|
+
encoded: list[dict] = []
|
33
|
+
|
34
|
+
def encode(obj: Any, path: list) -> Any:
|
35
|
+
if len(path) > MAX_DEPTH:
|
36
|
+
raise RuntimeError("I'm in too deep!")
|
37
|
+
|
38
|
+
if isinstance(obj, ZarrEncodable):
|
39
|
+
return encode(obj.to_dict(), path)
|
40
|
+
elif isinstance(obj, (list, tuple, set)):
|
41
|
+
out = (encode(item, [*path, idx]) for idx, item in enumerate(obj))
|
42
|
+
if isinstance(obj, tuple):
|
43
|
+
return tuple(out)
|
44
|
+
elif isinstance(obj, set):
|
45
|
+
return set(out)
|
46
|
+
else:
|
47
|
+
return list(out)
|
48
|
+
elif isinstance(obj, dict):
|
49
|
+
return {
|
50
|
+
dct_key: encode(dct_val, [*path, dct_key])
|
51
|
+
for dct_key, dct_val in obj.items()
|
52
|
+
}
|
53
|
+
elif isinstance(obj, PRIMITIVES):
|
54
|
+
return obj
|
55
|
+
elif isinstance(obj, np.ndarray):
|
56
|
+
new_name = str(max((int(i) + 1 for i in zarr_group.keys()), default=0))
|
57
|
+
zarr_group.create_dataset(name=new_name, data=obj)
|
58
|
+
encoded.append(
|
59
|
+
{
|
60
|
+
"path": path,
|
61
|
+
"dataset": new_name,
|
62
|
+
}
|
63
|
+
)
|
64
|
+
return None
|
65
|
+
else:
|
66
|
+
raise ValueError(f"unserializable type: {type(obj)}")
|
67
|
+
|
68
|
+
zarr_group.attrs["data"] = encode(data, [])
|
79
69
|
zarr_group.attrs["encoded"] = encoded
|
80
|
-
zarr_group.attrs["data"] = data
|
81
70
|
zarr_group.attrs["is_set"] = is_set
|
82
71
|
if is_pending_add:
|
83
72
|
zarr_group.attrs["is_pending_add"] = is_pending_add
|
@@ -88,100 +77,68 @@ def zarr_encode(data, zarr_group, is_pending_add, is_set):
|
|
88
77
|
|
89
78
|
def _zarr_encode_NEW(
|
90
79
|
obj: Any,
|
91
|
-
base_arr: zarr.Array,
|
92
80
|
root_group: zarr.Group,
|
93
81
|
arr_path: str,
|
94
|
-
|
95
|
-
arr_lookup=None,
|
96
|
-
):
|
82
|
+
) -> tuple[Any, list[list]]:
|
97
83
|
"""
|
98
84
|
Save arbitrarily-nested Python-primitive, `ZarrEncodable` and numpy array objects into
|
99
85
|
Zarr.
|
100
86
|
|
101
87
|
Parameters
|
102
88
|
----------
|
103
|
-
obj
|
104
|
-
|
105
|
-
|
106
|
-
encoder.
|
107
|
-
root_group
|
89
|
+
obj:
|
90
|
+
Object to encode.
|
91
|
+
root_group:
|
108
92
|
Parent Zarr group into which new Zarr arrays will be added (at `arr_path`).
|
109
|
-
arr_path
|
93
|
+
arr_path:
|
110
94
|
Path relative to `root_group` into which new Zarr arrays will be added.
|
111
95
|
|
112
96
|
Returns
|
113
97
|
-------
|
114
|
-
|
115
|
-
|
116
|
-
|
98
|
+
data
|
99
|
+
The encoded data.
|
100
|
+
arr_lookup
|
101
|
+
How to look up where to rebuild Numpy arrays.
|
117
102
|
"""
|
118
103
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
)
|
144
|
-
|
145
|
-
|
146
|
-
data = tuple(data)
|
147
|
-
elif isinstance(obj, set):
|
148
|
-
data = set(data)
|
149
|
-
|
150
|
-
elif isinstance(obj, dict):
|
151
|
-
data = {}
|
152
|
-
for dct_key, dct_val in obj.items():
|
153
|
-
dct_val, arr_lookup = _zarr_encode_NEW(
|
154
|
-
obj=dct_val,
|
155
|
-
base_arr=base_arr,
|
156
|
-
root_group=root_group,
|
157
|
-
arr_path=arr_path,
|
158
|
-
path=path + [dct_key],
|
159
|
-
)
|
160
|
-
data[dct_key] = dct_val
|
161
|
-
|
162
|
-
elif isinstance(obj, PRIMITIVES):
|
163
|
-
data = obj
|
164
|
-
|
165
|
-
elif isinstance(obj, np.ndarray):
|
166
|
-
# Might need to generate new group:
|
167
|
-
param_arr_group = root_group.require_group(arr_path)
|
168
|
-
names = [int(i) for i in param_arr_group.keys()]
|
169
|
-
if not names:
|
170
|
-
new_idx = 0
|
104
|
+
arr_lookup: list[list] = []
|
105
|
+
|
106
|
+
def encode(obj: Any, path: list) -> Any:
|
107
|
+
if len(path) > MAX_DEPTH:
|
108
|
+
raise RuntimeError("I'm in too deep!")
|
109
|
+
|
110
|
+
if isinstance(obj, ZarrEncodable):
|
111
|
+
return encode(obj.to_dict(), path)
|
112
|
+
elif isinstance(obj, (list, tuple, set)):
|
113
|
+
items = (encode(item, [*path, idx]) for idx, item in enumerate(obj))
|
114
|
+
if isinstance(obj, tuple):
|
115
|
+
return tuple(items)
|
116
|
+
elif isinstance(obj, set):
|
117
|
+
return set(items)
|
118
|
+
else:
|
119
|
+
return list(items)
|
120
|
+
elif isinstance(obj, dict):
|
121
|
+
return {key: encode(val, [*path, key]) for key, val in obj.items()}
|
122
|
+
elif isinstance(obj, PRIMITIVES):
|
123
|
+
return obj
|
124
|
+
elif isinstance(obj, np.ndarray):
|
125
|
+
# Might need to generate new group:
|
126
|
+
param_arr_group = root_group.require_group(arr_path)
|
127
|
+
new_idx = max((int(i) + 1 for i in param_arr_group.keys()), default=0)
|
128
|
+
param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj)
|
129
|
+
arr_lookup.append([path, new_idx])
|
130
|
+
return None
|
171
131
|
else:
|
172
|
-
|
173
|
-
param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj)
|
174
|
-
arr_lookup.append([path, new_idx])
|
175
|
-
data = None
|
132
|
+
raise ValueError(f"unserializable type: {type(obj)}")
|
176
133
|
|
177
|
-
return
|
134
|
+
return encode(obj, []), arr_lookup
|
178
135
|
|
179
136
|
|
180
137
|
def zarr_decode(
|
181
|
-
param_data:
|
138
|
+
param_data: None | dict,
|
182
139
|
arr_group: zarr.Group,
|
183
|
-
path=None,
|
184
|
-
dataset_copy=False,
|
140
|
+
path: list | None = None,
|
141
|
+
dataset_copy: bool = False,
|
185
142
|
):
|
186
143
|
"""
|
187
144
|
Decode data from a zarr group.
|
@@ -220,7 +177,7 @@ class ZarrEncodable:
|
|
220
177
|
|
221
178
|
_typ = None
|
222
179
|
|
223
|
-
def to_dict(self):
|
180
|
+
def to_dict(self) -> dict[str, Any]:
|
224
181
|
"""
|
225
182
|
Convert this object to a dict.
|
226
183
|
"""
|
@@ -228,18 +185,22 @@ class ZarrEncodable:
|
|
228
185
|
return dict(self.__dict__)
|
229
186
|
elif hasattr(self, "__slots__"):
|
230
187
|
return {k: getattr(self, k) for k in self.__slots__}
|
188
|
+
else:
|
189
|
+
# Should be unreachable
|
190
|
+
return {}
|
231
191
|
|
232
|
-
def to_zarr(self, zarr_group):
|
192
|
+
def to_zarr(self, zarr_group: zarr.Group):
|
233
193
|
"""
|
234
194
|
Save this object into the given zarr group.
|
235
195
|
"""
|
236
|
-
|
237
|
-
zarr_encode(data, zarr_group)
|
196
|
+
zarr_encode(self.to_dict(), zarr_group, is_pending_add=False, is_set=False)
|
238
197
|
|
239
198
|
@classmethod
|
240
|
-
def from_zarr(cls, zarr_group, dataset_copy=False):
|
199
|
+
def from_zarr(cls, zarr_group: zarr.Group, dataset_copy: bool = False) -> Self:
|
241
200
|
"""
|
242
201
|
Read an instance of this class from the given zarr group.
|
243
202
|
"""
|
244
|
-
|
203
|
+
# FIXME: Do the read of the data!
|
204
|
+
param_data = None
|
205
|
+
data = zarr_decode(param_data, zarr_group, dataset_copy=dataset_copy)
|
245
206
|
return cls(**data)
|
hpcflow/sdk/demo/cli.py
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
"""
|
2
|
+
CLI components for demonstration code.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
1
6
|
from pathlib import Path
|
2
7
|
from random import randint
|
8
|
+
from typing import TYPE_CHECKING
|
3
9
|
import click
|
4
10
|
|
5
11
|
from hpcflow.sdk.core.utils import get_process_stamp
|
@@ -20,10 +26,17 @@ from hpcflow.sdk.cli_common import (
|
|
20
26
|
cancel_opt,
|
21
27
|
submit_status_opt,
|
22
28
|
make_status_opt,
|
29
|
+
add_sub_opt,
|
23
30
|
)
|
24
31
|
|
32
|
+
if TYPE_CHECKING:
|
33
|
+
from collections.abc import Iterable
|
34
|
+
from typing import Literal
|
35
|
+
from ..app import BaseApp
|
36
|
+
from ..core.workflow import Workflow
|
37
|
+
|
25
38
|
|
26
|
-
def get_demo_software_CLI(app):
|
39
|
+
def get_demo_software_CLI(app: BaseApp):
|
27
40
|
"""Generate the CLI to provide an example software."""
|
28
41
|
|
29
42
|
@click.group()
|
@@ -35,7 +48,9 @@ def get_demo_software_CLI(app):
|
|
35
48
|
@click.option("--infile2", "-i2", type=click.Path(exists=True), required=True)
|
36
49
|
@click.option("--value", "-v")
|
37
50
|
@click.option("--out", "-o")
|
38
|
-
def demo_do_something(
|
51
|
+
def demo_do_something(
|
52
|
+
infile1: Path, infile2: Path, value: str | None = None, out: str | None = None
|
53
|
+
):
|
39
54
|
click.echo("trying to do something")
|
40
55
|
|
41
56
|
with Path(infile1).open("r") as handle:
|
@@ -47,7 +62,7 @@ def get_demo_software_CLI(app):
|
|
47
62
|
out = "outfile.txt"
|
48
63
|
out_path = Path(out)
|
49
64
|
with out_path.open("a") as handle:
|
50
|
-
handle.write("{}\n".format(randint(0, 1e6)))
|
65
|
+
handle.write("{}\n".format(randint(0, int(1e6))))
|
51
66
|
handle.write(
|
52
67
|
"{} Generated by `doSomething --infile1 {} --infile2 {}`.\n".format(
|
53
68
|
get_process_stamp(), infile1, infile2
|
@@ -69,10 +84,10 @@ def get_demo_software_CLI(app):
|
|
69
84
|
return demo_software
|
70
85
|
|
71
86
|
|
72
|
-
def get_demo_workflow_CLI(app):
|
87
|
+
def get_demo_workflow_CLI(app: BaseApp):
|
73
88
|
"""Generate the CLI to provide access to builtin demo workflows."""
|
74
89
|
|
75
|
-
def list_callback(ctx, param, value):
|
90
|
+
def list_callback(ctx: click.Context, param, value: bool):
|
76
91
|
if not value or ctx.resilient_parsing:
|
77
92
|
return
|
78
93
|
# TODO: format with Rich with a one-line description
|
@@ -104,17 +119,19 @@ def get_demo_workflow_CLI(app):
|
|
104
119
|
@ts_name_fmt_option
|
105
120
|
@variables_option
|
106
121
|
@make_status_opt
|
122
|
+
@add_sub_opt
|
107
123
|
def make_demo_workflow(
|
108
|
-
workflow_name,
|
109
|
-
format,
|
110
|
-
path,
|
111
|
-
name,
|
112
|
-
overwrite,
|
113
|
-
store,
|
114
|
-
ts_fmt=None,
|
115
|
-
ts_name_fmt=None,
|
116
|
-
variables=
|
117
|
-
status=True,
|
124
|
+
workflow_name: str,
|
125
|
+
format: Literal["json", "yaml"] | None,
|
126
|
+
path: Path | None,
|
127
|
+
name: str | None,
|
128
|
+
overwrite: bool,
|
129
|
+
store: str,
|
130
|
+
ts_fmt: str | None = None,
|
131
|
+
ts_name_fmt: str | None = None,
|
132
|
+
variables: Iterable[tuple[str, str]] = (),
|
133
|
+
status: bool = True,
|
134
|
+
add_submission: bool = False,
|
118
135
|
):
|
119
136
|
wk = app.make_demo_workflow(
|
120
137
|
workflow_name=workflow_name,
|
@@ -127,7 +144,9 @@ def get_demo_workflow_CLI(app):
|
|
127
144
|
ts_name_fmt=ts_name_fmt,
|
128
145
|
variables=dict(variables),
|
129
146
|
status=status,
|
147
|
+
add_submission=add_submission,
|
130
148
|
)
|
149
|
+
assert isinstance(wk, Workflow)
|
131
150
|
click.echo(wk.path)
|
132
151
|
|
133
152
|
@demo_workflow.command("go")
|
@@ -148,22 +167,22 @@ def get_demo_workflow_CLI(app):
|
|
148
167
|
@cancel_opt
|
149
168
|
@submit_status_opt
|
150
169
|
def make_and_submit_demo_workflow(
|
151
|
-
workflow_name,
|
152
|
-
format,
|
153
|
-
path,
|
154
|
-
name,
|
155
|
-
overwrite,
|
156
|
-
store,
|
157
|
-
ts_fmt=None,
|
158
|
-
ts_name_fmt=None,
|
159
|
-
variables=
|
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,
|
170
|
+
workflow_name: str,
|
171
|
+
format: Literal["json", "yaml"] | None,
|
172
|
+
path: Path | None,
|
173
|
+
name: str | None,
|
174
|
+
overwrite: bool,
|
175
|
+
store: str,
|
176
|
+
ts_fmt: str | None = None,
|
177
|
+
ts_name_fmt: str | None = None,
|
178
|
+
variables: Iterable[tuple[str, str]] = (),
|
179
|
+
js_parallelism: bool | None = None,
|
180
|
+
wait: bool = False,
|
181
|
+
add_to_known: bool = True,
|
182
|
+
print_idx: bool = False,
|
183
|
+
tasks: list[int] | None = None,
|
184
|
+
cancel: bool = False,
|
185
|
+
status: bool = True,
|
167
186
|
):
|
168
187
|
out = app.make_and_submit_demo_workflow(
|
169
188
|
workflow_name=workflow_name,
|
@@ -184,20 +203,21 @@ def get_demo_workflow_CLI(app):
|
|
184
203
|
status=status,
|
185
204
|
)
|
186
205
|
if print_idx:
|
206
|
+
assert isinstance(out, tuple)
|
187
207
|
click.echo(out[1])
|
188
208
|
|
189
209
|
@demo_workflow.command("copy")
|
190
210
|
@click.argument("workflow_name")
|
191
211
|
@click.argument("destination")
|
192
212
|
@click.option("--doc/--no-doc", default=True)
|
193
|
-
def copy_demo_workflow(workflow_name, destination, doc):
|
213
|
+
def copy_demo_workflow(workflow_name: str, destination: str, doc: bool):
|
194
214
|
app.copy_demo_workflow(name=workflow_name, dst=destination, doc=doc)
|
195
215
|
|
196
216
|
@demo_workflow.command("show")
|
197
217
|
@click.argument("workflow_name")
|
198
218
|
@click.option("--syntax/--no-syntax", default=True)
|
199
219
|
@click.option("--doc/--no-doc", default=True)
|
200
|
-
def show_demo_workflow(workflow_name, syntax, doc):
|
220
|
+
def show_demo_workflow(workflow_name: str, syntax: bool, doc: bool):
|
201
221
|
app.show_demo_workflow(workflow_name, syntax=syntax, doc=doc)
|
202
222
|
|
203
223
|
return demo_workflow
|
hpcflow/sdk/helper/cli.py
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
Common Click command line options related to the helper.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from
|
5
|
+
from __future__ import annotations
|
6
|
+
from typing import TYPE_CHECKING
|
6
7
|
|
7
8
|
import click
|
8
|
-
import psutil
|
9
9
|
|
10
|
-
from .helper import (
|
10
|
+
from hpcflow.sdk.helper.helper import (
|
11
11
|
DEFAULT_TIMEOUT,
|
12
12
|
DEFAULT_TIMEOUT_CHECK,
|
13
13
|
DEFAULT_WATCH_INTERVAL,
|
@@ -22,7 +22,10 @@ from .helper import (
|
|
22
22
|
get_helper_PID,
|
23
23
|
get_helper_uptime,
|
24
24
|
)
|
25
|
-
from
|
25
|
+
from hpcflow.sdk.cli_common import _add_doc_from_help
|
26
|
+
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
from ..app import BaseApp
|
26
29
|
|
27
30
|
#: Helper option: ``--timeout``
|
28
31
|
timeout_option = click.option(
|
@@ -54,7 +57,7 @@ watch_interval_option = click.option(
|
|
54
57
|
_add_doc_from_help(timeout_option, timeout_check_interval_option, watch_interval_option)
|
55
58
|
|
56
59
|
|
57
|
-
def get_helper_CLI(app):
|
60
|
+
def get_helper_CLI(app: BaseApp):
|
58
61
|
"""Generate the CLI to provide some server-like functionality."""
|
59
62
|
|
60
63
|
@click.group()
|
@@ -65,7 +68,7 @@ def get_helper_CLI(app):
|
|
65
68
|
@timeout_option
|
66
69
|
@timeout_check_interval_option
|
67
70
|
@watch_interval_option
|
68
|
-
def start(timeout, timeout_check_interval, watch_interval):
|
71
|
+
def start(timeout: float, timeout_check_interval: float, watch_interval: float):
|
69
72
|
"""Start the helper process."""
|
70
73
|
start_helper(app, timeout, timeout_check_interval, watch_interval)
|
71
74
|
|
@@ -78,7 +81,7 @@ def get_helper_CLI(app):
|
|
78
81
|
@timeout_option
|
79
82
|
@timeout_check_interval_option
|
80
83
|
@watch_interval_option
|
81
|
-
def run(timeout, timeout_check_interval, watch_interval):
|
84
|
+
def run(timeout: float, timeout_check_interval: float, watch_interval: float):
|
82
85
|
"""Run the helper functionality."""
|
83
86
|
run_helper(app, timeout, timeout_check_interval, watch_interval)
|
84
87
|
|
@@ -86,13 +89,13 @@ def get_helper_CLI(app):
|
|
86
89
|
@timeout_option
|
87
90
|
@timeout_check_interval_option
|
88
91
|
@watch_interval_option
|
89
|
-
def restart(timeout, timeout_check_interval, watch_interval):
|
92
|
+
def restart(timeout: float, timeout_check_interval: float, watch_interval: float):
|
90
93
|
"""Restart (or start) the helper process."""
|
91
94
|
restart_helper(app, timeout, timeout_check_interval, watch_interval)
|
92
95
|
|
93
96
|
@helper.command()
|
94
97
|
@click.option("-f", "--file", is_flag=True)
|
95
|
-
def pid(file):
|
98
|
+
def pid(file: bool):
|
96
99
|
"""Get the process ID of the running helper, if running."""
|
97
100
|
pid_info = get_helper_PID(app)
|
98
101
|
if pid_info:
|
@@ -103,32 +106,32 @@ def get_helper_CLI(app):
|
|
103
106
|
click.echo(pid)
|
104
107
|
|
105
108
|
@helper.command()
|
106
|
-
def clear():
|
109
|
+
def clear() -> None:
|
107
110
|
"""Remove the PID file (and kill the helper process if it exists). This should not
|
108
111
|
normally be needed."""
|
109
112
|
clear_helper(app)
|
110
113
|
|
111
114
|
@helper.command()
|
112
|
-
def uptime():
|
115
|
+
def uptime() -> None:
|
113
116
|
"""Get the uptime of the helper process, if it is running."""
|
114
117
|
out = get_helper_uptime(app)
|
115
118
|
if out:
|
116
119
|
click.echo(out)
|
117
120
|
|
118
121
|
@helper.command()
|
119
|
-
def log_path():
|
122
|
+
def log_path() -> None:
|
120
123
|
"""Get the path to the helper log file (may not exist)."""
|
121
124
|
click.echo(get_helper_log_path(app))
|
122
125
|
|
123
126
|
@helper.command()
|
124
|
-
def watch_list_path():
|
127
|
+
def watch_list_path() -> None:
|
125
128
|
"""Get the path to the workflow watch list file (may not exist)."""
|
126
129
|
click.echo(get_watcher_file_path(app))
|
127
130
|
|
128
131
|
@helper.command()
|
129
|
-
def watch_list():
|
132
|
+
def watch_list() -> None:
|
130
133
|
"""Get the list of workflows currently being watched."""
|
131
|
-
for wk in get_helper_watch_list(app) or
|
134
|
+
for wk in get_helper_watch_list(app) or ():
|
132
135
|
click.echo(str(wk["path"]))
|
133
136
|
|
134
137
|
return helper
|