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.
Files changed (176) hide show
  1. hpcflow/__pyinstaller/hook-hpcflow.py +9 -6
  2. hpcflow/_version.py +1 -1
  3. hpcflow/app.py +1 -0
  4. hpcflow/data/scripts/bad_script.py +2 -0
  5. hpcflow/data/scripts/do_nothing.py +2 -0
  6. hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
  7. hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
  8. hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
  9. hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
  10. hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
  11. hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
  12. hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
  13. hpcflow/data/scripts/input_file_generator_basic.py +3 -0
  14. hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
  15. hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
  16. hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
  17. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
  18. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
  19. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
  20. hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
  21. hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
  22. hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
  23. hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
  24. hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
  25. hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
  26. hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
  27. hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
  28. hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
  29. hpcflow/data/scripts/output_file_parser_basic.py +3 -0
  30. hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
  31. hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
  32. hpcflow/data/scripts/script_exit_test.py +5 -0
  33. hpcflow/data/template_components/environments.yaml +1 -1
  34. hpcflow/sdk/__init__.py +26 -15
  35. hpcflow/sdk/app.py +2192 -768
  36. hpcflow/sdk/cli.py +506 -296
  37. hpcflow/sdk/cli_common.py +105 -7
  38. hpcflow/sdk/config/__init__.py +1 -1
  39. hpcflow/sdk/config/callbacks.py +115 -43
  40. hpcflow/sdk/config/cli.py +126 -103
  41. hpcflow/sdk/config/config.py +674 -318
  42. hpcflow/sdk/config/config_file.py +131 -95
  43. hpcflow/sdk/config/errors.py +125 -84
  44. hpcflow/sdk/config/types.py +148 -0
  45. hpcflow/sdk/core/__init__.py +25 -1
  46. hpcflow/sdk/core/actions.py +1771 -1059
  47. hpcflow/sdk/core/app_aware.py +24 -0
  48. hpcflow/sdk/core/cache.py +139 -79
  49. hpcflow/sdk/core/command_files.py +263 -287
  50. hpcflow/sdk/core/commands.py +145 -112
  51. hpcflow/sdk/core/element.py +828 -535
  52. hpcflow/sdk/core/enums.py +192 -0
  53. hpcflow/sdk/core/environment.py +74 -93
  54. hpcflow/sdk/core/errors.py +455 -52
  55. hpcflow/sdk/core/execute.py +207 -0
  56. hpcflow/sdk/core/json_like.py +540 -272
  57. hpcflow/sdk/core/loop.py +751 -347
  58. hpcflow/sdk/core/loop_cache.py +164 -47
  59. hpcflow/sdk/core/object_list.py +370 -207
  60. hpcflow/sdk/core/parameters.py +1100 -627
  61. hpcflow/sdk/core/rule.py +59 -41
  62. hpcflow/sdk/core/run_dir_files.py +21 -37
  63. hpcflow/sdk/core/skip_reason.py +7 -0
  64. hpcflow/sdk/core/task.py +1649 -1339
  65. hpcflow/sdk/core/task_schema.py +308 -196
  66. hpcflow/sdk/core/test_utils.py +191 -114
  67. hpcflow/sdk/core/types.py +440 -0
  68. hpcflow/sdk/core/utils.py +485 -309
  69. hpcflow/sdk/core/validation.py +82 -9
  70. hpcflow/sdk/core/workflow.py +2544 -1178
  71. hpcflow/sdk/core/zarr_io.py +98 -137
  72. hpcflow/sdk/data/workflow_spec_schema.yaml +2 -0
  73. hpcflow/sdk/demo/cli.py +53 -33
  74. hpcflow/sdk/helper/cli.py +18 -15
  75. hpcflow/sdk/helper/helper.py +75 -63
  76. hpcflow/sdk/helper/watcher.py +61 -28
  77. hpcflow/sdk/log.py +122 -71
  78. hpcflow/sdk/persistence/__init__.py +8 -31
  79. hpcflow/sdk/persistence/base.py +1360 -606
  80. hpcflow/sdk/persistence/defaults.py +6 -0
  81. hpcflow/sdk/persistence/discovery.py +38 -0
  82. hpcflow/sdk/persistence/json.py +568 -188
  83. hpcflow/sdk/persistence/pending.py +382 -179
  84. hpcflow/sdk/persistence/store_resource.py +39 -23
  85. hpcflow/sdk/persistence/types.py +318 -0
  86. hpcflow/sdk/persistence/utils.py +14 -11
  87. hpcflow/sdk/persistence/zarr.py +1337 -433
  88. hpcflow/sdk/runtime.py +44 -41
  89. hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
  90. hpcflow/sdk/submission/jobscript.py +1651 -692
  91. hpcflow/sdk/submission/schedulers/__init__.py +167 -39
  92. hpcflow/sdk/submission/schedulers/direct.py +121 -81
  93. hpcflow/sdk/submission/schedulers/sge.py +170 -129
  94. hpcflow/sdk/submission/schedulers/slurm.py +291 -268
  95. hpcflow/sdk/submission/schedulers/utils.py +12 -2
  96. hpcflow/sdk/submission/shells/__init__.py +14 -15
  97. hpcflow/sdk/submission/shells/base.py +150 -29
  98. hpcflow/sdk/submission/shells/bash.py +283 -173
  99. hpcflow/sdk/submission/shells/os_version.py +31 -30
  100. hpcflow/sdk/submission/shells/powershell.py +228 -170
  101. hpcflow/sdk/submission/submission.py +1014 -335
  102. hpcflow/sdk/submission/types.py +140 -0
  103. hpcflow/sdk/typing.py +182 -12
  104. hpcflow/sdk/utils/arrays.py +71 -0
  105. hpcflow/sdk/utils/deferred_file.py +55 -0
  106. hpcflow/sdk/utils/hashing.py +16 -0
  107. hpcflow/sdk/utils/patches.py +12 -0
  108. hpcflow/sdk/utils/strings.py +33 -0
  109. hpcflow/tests/api/test_api.py +32 -0
  110. hpcflow/tests/conftest.py +27 -6
  111. hpcflow/tests/data/multi_path_sequences.yaml +29 -0
  112. hpcflow/tests/data/workflow_test_run_abort.yaml +34 -35
  113. hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
  114. hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
  115. hpcflow/tests/scripts/test_input_file_generators.py +282 -0
  116. hpcflow/tests/scripts/test_main_scripts.py +866 -85
  117. hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
  118. hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
  119. hpcflow/tests/shells/wsl/test_wsl_submission.py +12 -4
  120. hpcflow/tests/unit/test_action.py +262 -75
  121. hpcflow/tests/unit/test_action_rule.py +9 -4
  122. hpcflow/tests/unit/test_app.py +33 -6
  123. hpcflow/tests/unit/test_cache.py +46 -0
  124. hpcflow/tests/unit/test_cli.py +134 -1
  125. hpcflow/tests/unit/test_command.py +71 -54
  126. hpcflow/tests/unit/test_config.py +142 -16
  127. hpcflow/tests/unit/test_config_file.py +21 -18
  128. hpcflow/tests/unit/test_element.py +58 -62
  129. hpcflow/tests/unit/test_element_iteration.py +50 -1
  130. hpcflow/tests/unit/test_element_set.py +29 -19
  131. hpcflow/tests/unit/test_group.py +4 -2
  132. hpcflow/tests/unit/test_input_source.py +116 -93
  133. hpcflow/tests/unit/test_input_value.py +29 -24
  134. hpcflow/tests/unit/test_jobscript_unit.py +757 -0
  135. hpcflow/tests/unit/test_json_like.py +44 -35
  136. hpcflow/tests/unit/test_loop.py +1396 -84
  137. hpcflow/tests/unit/test_meta_task.py +325 -0
  138. hpcflow/tests/unit/test_multi_path_sequences.py +229 -0
  139. hpcflow/tests/unit/test_object_list.py +17 -12
  140. hpcflow/tests/unit/test_parameter.py +29 -7
  141. hpcflow/tests/unit/test_persistence.py +237 -42
  142. hpcflow/tests/unit/test_resources.py +20 -18
  143. hpcflow/tests/unit/test_run.py +117 -6
  144. hpcflow/tests/unit/test_run_directories.py +29 -0
  145. hpcflow/tests/unit/test_runtime.py +2 -1
  146. hpcflow/tests/unit/test_schema_input.py +23 -15
  147. hpcflow/tests/unit/test_shell.py +23 -2
  148. hpcflow/tests/unit/test_slurm.py +8 -7
  149. hpcflow/tests/unit/test_submission.py +38 -89
  150. hpcflow/tests/unit/test_task.py +352 -247
  151. hpcflow/tests/unit/test_task_schema.py +33 -20
  152. hpcflow/tests/unit/test_utils.py +9 -11
  153. hpcflow/tests/unit/test_value_sequence.py +15 -12
  154. hpcflow/tests/unit/test_workflow.py +114 -83
  155. hpcflow/tests/unit/test_workflow_template.py +0 -1
  156. hpcflow/tests/unit/utils/test_arrays.py +40 -0
  157. hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
  158. hpcflow/tests/unit/utils/test_hashing.py +65 -0
  159. hpcflow/tests/unit/utils/test_patches.py +5 -0
  160. hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
  161. hpcflow/tests/workflows/__init__.py +0 -0
  162. hpcflow/tests/workflows/test_directory_structure.py +31 -0
  163. hpcflow/tests/workflows/test_jobscript.py +334 -1
  164. hpcflow/tests/workflows/test_run_status.py +198 -0
  165. hpcflow/tests/workflows/test_skip_downstream.py +696 -0
  166. hpcflow/tests/workflows/test_submission.py +140 -0
  167. hpcflow/tests/workflows/test_workflows.py +160 -15
  168. hpcflow/tests/workflows/test_zip.py +18 -0
  169. hpcflow/viz_demo.ipynb +6587 -3
  170. {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/METADATA +8 -4
  171. hpcflow_new2-0.2.0a199.dist-info/RECORD +221 -0
  172. hpcflow/sdk/core/parallel.py +0 -21
  173. hpcflow_new2-0.2.0a189.dist-info/RECORD +0 -158
  174. {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/LICENSE +0 -0
  175. {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/WHEEL +0 -0
  176. {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/entry_points.txt +0 -0
@@ -2,82 +2,71 @@
2
2
  Utilities for working with Zarr.
3
3
  """
4
4
 
5
- from typing import Any, Dict, Union
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
- PRIMITIVES = (
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
- def _zarr_encode(obj, zarr_group, path=None, encoded=None):
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
- data, encoded = _zarr_encode(data, zarr_group)
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
- path=None,
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
- base_arr
105
- Zarr object array into which (the top-level) `obj` is saved using the MsgPack
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
- (data, arr_lookup)
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
- path = path or []
120
- arr_lookup = arr_lookup or []
121
-
122
- if len(path) > 50:
123
- raise RuntimeError("I'm in too deep!")
124
-
125
- if isinstance(obj, ZarrEncodable):
126
- data, arr_lookup = _zarr_encode_NEW(
127
- obj=obj.to_dict(),
128
- base_arr=base_arr,
129
- root_group=root_group,
130
- arr_path=arr_path,
131
- path=path,
132
- )
133
-
134
- elif isinstance(obj, (list, tuple, set)):
135
- data = []
136
- for idx, item in enumerate(obj):
137
- item, arr_lookup = _zarr_encode_NEW(
138
- obj=item,
139
- base_arr=base_arr,
140
- root_group=root_group,
141
- arr_path=arr_path,
142
- path=path + [idx],
143
- )
144
- data.append(item)
145
- if isinstance(obj, tuple):
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
- new_idx = max(names) + 1
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 data, arr_lookup
134
+ return encode(obj, []), arr_lookup
178
135
 
179
136
 
180
137
  def zarr_decode(
181
- param_data: Union[None, Dict],
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
- data = self.to_dict()
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
- data = zarr_decode(zarr_group, dataset_copy=dataset_copy)
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)
@@ -6,10 +6,12 @@ rules:
6
6
  - name
7
7
  - source_file
8
8
  - resources
9
+ - config
9
10
  - environments
10
11
  - env_presets
11
12
  - template_components
12
13
  - tasks
14
+ - meta_tasks
13
15
  - loops
14
16
  - store_kwargs
15
17
  - merge_resources
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(infile1, infile2, value=None, out=None):
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=None,
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=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,
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 datetime import timedelta
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 ..cli_common import _add_doc_from_help
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