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
@@ -2,14 +2,22 @@
|
|
2
2
|
Models of data stores as resources.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
5
6
|
from abc import ABC, abstractmethod
|
6
7
|
import copy
|
7
8
|
import json
|
8
|
-
from
|
9
|
-
from typing import Callable, Union
|
9
|
+
from typing import Any, Callable, TYPE_CHECKING
|
10
10
|
|
11
11
|
from hpcflow.sdk.core.utils import get_md5_hash
|
12
12
|
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from collections.abc import Mapping
|
15
|
+
from logging import Logger
|
16
|
+
from pathlib import Path
|
17
|
+
import zarr # type: ignore
|
18
|
+
from fsspec import AbstractFileSystem # type: ignore
|
19
|
+
from ..app import BaseApp
|
20
|
+
|
13
21
|
|
14
22
|
class StoreResource(ABC):
|
15
23
|
"""Class to represent a persistent resource within which store data lives.
|
@@ -25,39 +33,42 @@ class StoreResource(ABC):
|
|
25
33
|
The store name.
|
26
34
|
"""
|
27
35
|
|
28
|
-
def __init__(self, app, name: str) -> None:
|
29
|
-
self.
|
36
|
+
def __init__(self, app: BaseApp, name: str) -> None:
|
37
|
+
self._app = app
|
30
38
|
self.name = name
|
31
|
-
self.data = {"read": None, "update": None}
|
39
|
+
self.data: dict[str, Any] = {"read": None, "update": None}
|
32
40
|
self.hash = None
|
33
41
|
|
34
42
|
def __repr__(self) -> str:
|
35
43
|
return f"{self.__class__.__name__}(name={self.name!r})"
|
36
44
|
|
37
45
|
@property
|
38
|
-
def logger(self):
|
46
|
+
def logger(self) -> Logger:
|
39
47
|
"""
|
40
48
|
The logger.
|
41
49
|
"""
|
42
|
-
return self.
|
50
|
+
return self._app.persistence_logger
|
43
51
|
|
44
52
|
@abstractmethod
|
45
|
-
def _load(self):
|
53
|
+
def _load(self) -> Any:
|
46
54
|
pass
|
47
55
|
|
48
56
|
@abstractmethod
|
49
|
-
def _dump(self, data):
|
57
|
+
def _dump(self, data: dict | list):
|
50
58
|
pass
|
51
59
|
|
52
|
-
def open(self, action):
|
60
|
+
def open(self, action: str):
|
53
61
|
"""
|
54
62
|
Open the store.
|
55
63
|
|
56
64
|
Parameters
|
57
65
|
----------
|
58
|
-
action:
|
66
|
+
action:
|
59
67
|
What we are opening the store for; typically either ``read`` or ``update``.
|
60
68
|
"""
|
69
|
+
|
70
|
+
# TODO: some tests?
|
71
|
+
|
61
72
|
if action == "read":
|
62
73
|
# reuse "update" data if set, rather than re-loading from disk -- but copy,
|
63
74
|
# so changes made in the "read" scope do not update!
|
@@ -80,17 +91,17 @@ class StoreResource(ABC):
|
|
80
91
|
self.data[action] = data
|
81
92
|
|
82
93
|
try:
|
83
|
-
self.hash = get_md5_hash(data)
|
94
|
+
self.hash = get_md5_hash(data) # type: ignore
|
84
95
|
except Exception:
|
85
96
|
pass
|
86
97
|
|
87
|
-
def close(self, action):
|
98
|
+
def close(self, action: str):
|
88
99
|
"""
|
89
100
|
Close the store for a particular action.
|
90
101
|
|
91
102
|
Parameters
|
92
103
|
----------
|
93
|
-
action:
|
104
|
+
action:
|
94
105
|
What we are closing the store for.
|
95
106
|
Should match a previous call to :py:meth:`close`.
|
96
107
|
"""
|
@@ -135,25 +146,30 @@ class JSONFileStoreResource(StoreResource):
|
|
135
146
|
The filesystem that the JSON file resides within.
|
136
147
|
"""
|
137
148
|
|
138
|
-
def __init__(
|
149
|
+
def __init__(
|
150
|
+
self,
|
151
|
+
app: BaseApp,
|
152
|
+
name: str,
|
153
|
+
filename: str,
|
154
|
+
path: str | Path,
|
155
|
+
fs: AbstractFileSystem,
|
156
|
+
):
|
139
157
|
self.filename = filename
|
140
158
|
self.path = path
|
141
159
|
self.fs = fs
|
142
160
|
super().__init__(app, name)
|
143
161
|
|
144
162
|
@property
|
145
|
-
def _full_path(self):
|
163
|
+
def _full_path(self) -> str:
|
146
164
|
return f"{self.path}/{self.filename}"
|
147
165
|
|
148
|
-
def _load(self):
|
166
|
+
def _load(self) -> Any:
|
149
167
|
self.logger.debug(f"{self!r}: loading JSON from file.")
|
150
168
|
with self.fs.open(self._full_path, mode="rt") as fp:
|
151
169
|
return json.load(fp)
|
152
170
|
|
153
|
-
def _dump(self, data):
|
171
|
+
def _dump(self, data: Mapping | list):
|
154
172
|
self.logger.debug(f"{self!r}: dumping JSON to file")
|
155
|
-
if "runs" in data:
|
156
|
-
self.logger.debug(f"...runs: {data['runs']}")
|
157
173
|
with self.fs.open(self._full_path, mode="wt") as fp:
|
158
174
|
json.dump(data, fp, indent=2)
|
159
175
|
|
@@ -172,16 +188,16 @@ class ZarrAttrsStoreResource(StoreResource):
|
|
172
188
|
How to actually perform an open on the underlying resource.
|
173
189
|
"""
|
174
190
|
|
175
|
-
def __init__(self, app, name: str, open_call: Callable):
|
191
|
+
def __init__(self, app: BaseApp, name: str, open_call: Callable[..., zarr.Group]):
|
176
192
|
self.open_call = open_call
|
177
193
|
super().__init__(app, name)
|
178
194
|
|
179
|
-
def _load(self):
|
195
|
+
def _load(self) -> Any:
|
180
196
|
self.logger.debug(f"{self!r}: loading Zarr attributes.")
|
181
197
|
item = self.open_call(mode="r")
|
182
198
|
return copy.deepcopy(item.attrs.asdict())
|
183
199
|
|
184
|
-
def _dump(self, data):
|
200
|
+
def _dump(self, data: dict | list):
|
185
201
|
self.logger.debug(f"{self!r}: dumping Zarr attributes.")
|
186
202
|
item = self.open_call(mode="r+")
|
187
203
|
item.attrs.put(data)
|
@@ -0,0 +1,318 @@
|
|
1
|
+
"""
|
2
|
+
Types used in type-checking the persistence subsystem.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
6
|
+
from typing import Any, Generic, TypeVar, TYPE_CHECKING
|
7
|
+
from typing_extensions import TypedDict, NotRequired, TypeAlias
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from collections.abc import Mapping
|
11
|
+
from .base import StoreTask, StoreElement, StoreElementIter, StoreEAR, StoreParameter
|
12
|
+
from ..core.json_like import JSONed
|
13
|
+
from ..core.parameters import ParameterValue
|
14
|
+
from ..core.types import IterableParam
|
15
|
+
from ..typing import DataIndex, ParamSource
|
16
|
+
|
17
|
+
#: Bound type variable: :class:`StoreTask`.
|
18
|
+
AnySTask = TypeVar("AnySTask", bound="StoreTask")
|
19
|
+
#: Bound type variable: :class:`StoreElement`.
|
20
|
+
AnySElement = TypeVar("AnySElement", bound="StoreElement")
|
21
|
+
#: Bound type variable: :class:`StoreElementITer`.
|
22
|
+
AnySElementIter = TypeVar("AnySElementIter", bound="StoreElementIter")
|
23
|
+
#: Bound type variable: :class:`StoreEAR`.
|
24
|
+
AnySEAR = TypeVar("AnySEAR", bound="StoreEAR")
|
25
|
+
#: Bound type variable: :class:`StoreParameter`.
|
26
|
+
AnySParameter = TypeVar("AnySParameter", bound="StoreParameter")
|
27
|
+
#: Type of possible stored parameters.
|
28
|
+
ParameterTypes: TypeAlias = (
|
29
|
+
"ParameterValue | list | tuple | set | dict | int | float | str | None | Any"
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
class File(TypedDict):
|
34
|
+
"""
|
35
|
+
Descriptor for file metadata.
|
36
|
+
"""
|
37
|
+
|
38
|
+
#: Whether to store the contents.
|
39
|
+
store_contents: bool
|
40
|
+
#: The path to the file.
|
41
|
+
path: str
|
42
|
+
|
43
|
+
|
44
|
+
class FileDescriptor(TypedDict):
|
45
|
+
"""
|
46
|
+
Descriptor for file metadata.
|
47
|
+
"""
|
48
|
+
|
49
|
+
#: Whether this is an input file.
|
50
|
+
is_input: bool
|
51
|
+
#: Whether to store the contents.
|
52
|
+
store_contents: bool
|
53
|
+
#: Where the file will go.
|
54
|
+
dst_path: str
|
55
|
+
#: The path to the file.
|
56
|
+
path: str | None
|
57
|
+
#: Whether to delete the file after processing.
|
58
|
+
clean_up: bool
|
59
|
+
# The contents of the file.
|
60
|
+
contents: NotRequired[str]
|
61
|
+
|
62
|
+
|
63
|
+
class LoopDescriptor(TypedDict):
|
64
|
+
"""
|
65
|
+
Descriptor for loop metadata.
|
66
|
+
"""
|
67
|
+
|
68
|
+
#: The parameters iterated over by the loop.
|
69
|
+
iterable_parameters: dict[str, IterableParam]
|
70
|
+
#: The parameters output by the loop, and the final task insert ID from which they
|
71
|
+
#: are output.
|
72
|
+
output_parameters: dict[str, int]
|
73
|
+
#: The template data from which the loop was created.
|
74
|
+
loop_template: NotRequired[dict[str, Any]]
|
75
|
+
#: The number of iterations generated by a loop.
|
76
|
+
#: Note that the type is really ``list[tuple[tuple[int, ...], int]]``
|
77
|
+
#: but the persistence implementations don't handle tuples usefully.
|
78
|
+
num_added_iterations: list[list[list[int] | int]]
|
79
|
+
#: The parents of the loop.
|
80
|
+
parents: list[str]
|
81
|
+
|
82
|
+
|
83
|
+
# TODO: This type looks familiar...
|
84
|
+
class StoreCreationInfo(TypedDict):
|
85
|
+
"""
|
86
|
+
Information about the creation of the persistence store.
|
87
|
+
"""
|
88
|
+
|
89
|
+
#: Information about the application.
|
90
|
+
app_info: dict[str, Any]
|
91
|
+
#: When the persistence store was created.
|
92
|
+
create_time: str
|
93
|
+
#: The unique identifier for for the store/workflow.
|
94
|
+
id: str
|
95
|
+
|
96
|
+
|
97
|
+
class ElemMeta(TypedDict):
|
98
|
+
"""
|
99
|
+
The kwargs supported for a StoreElement.
|
100
|
+
"""
|
101
|
+
|
102
|
+
#: The ID of the element.
|
103
|
+
id_: int
|
104
|
+
#: The index of the element.
|
105
|
+
index: int
|
106
|
+
#: The index of the element in its element set.
|
107
|
+
es_idx: int
|
108
|
+
#: The indices of the element in the sequences that contain it.
|
109
|
+
seq_idx: dict[str, int]
|
110
|
+
#: The indices of the element's sources.
|
111
|
+
src_idx: dict[str, int]
|
112
|
+
#: The task associated with the element.
|
113
|
+
task_ID: int
|
114
|
+
#: The iteration IDs.
|
115
|
+
iteration_IDs: list[int]
|
116
|
+
|
117
|
+
|
118
|
+
class IterMeta(TypedDict):
|
119
|
+
"""
|
120
|
+
The kwargs supported for a StoreElementIter.
|
121
|
+
"""
|
122
|
+
|
123
|
+
#: The index of the iteration.
|
124
|
+
data_idx: DataIndex
|
125
|
+
#: The EARs associated with the iteration.
|
126
|
+
EAR_IDs: dict[int, list[int]]
|
127
|
+
#: Whether the EARs have been initialised.
|
128
|
+
EARs_initialised: bool
|
129
|
+
#: The ID of the element.
|
130
|
+
element_ID: int
|
131
|
+
#: The loops containing the iteration.
|
132
|
+
loop_idx: dict[str, int]
|
133
|
+
#: The schema parameters being iterated over.
|
134
|
+
schema_parameters: list[str]
|
135
|
+
|
136
|
+
|
137
|
+
class RunMeta(TypedDict):
|
138
|
+
"""
|
139
|
+
The kwargs supported for StoreEAR.
|
140
|
+
"""
|
141
|
+
|
142
|
+
#: The ID of the EAR.
|
143
|
+
id_: int
|
144
|
+
#: The ID of the element iteration containing the EAR.
|
145
|
+
elem_iter_ID: int
|
146
|
+
#: The index of the action that generated the EAR.
|
147
|
+
action_idx: int
|
148
|
+
#: The commands that the EAR will run.
|
149
|
+
commands_idx: list[int]
|
150
|
+
#: The data handled by the EAR.
|
151
|
+
data_idx: DataIndex
|
152
|
+
#: Metadata about the EAR.
|
153
|
+
metadata: Metadata | None
|
154
|
+
#: When the EAR ended, if known.
|
155
|
+
end_time: NotRequired[str | None]
|
156
|
+
#: The exit code of the EAR, if known.
|
157
|
+
exit_code: int | None
|
158
|
+
#: When the EAR started, if known.
|
159
|
+
start_time: NotRequired[str | None]
|
160
|
+
#: Working directory snapshot at start.
|
161
|
+
snapshot_start: dict[str, Any] | None
|
162
|
+
#: Working directory snapshot at end.
|
163
|
+
snapshot_end: dict[str, Any] | None
|
164
|
+
#: The index of the EAR in the submissions.
|
165
|
+
submission_idx: int | None
|
166
|
+
#: Where the EAR is set to run.
|
167
|
+
run_hostname: str | None
|
168
|
+
#: Whether the EAR succeeded, if known.
|
169
|
+
success: bool | None
|
170
|
+
#: The skip reason ID, if EAR was skipped.
|
171
|
+
skip: int
|
172
|
+
#: Port number used by ZeroMQ during execution.
|
173
|
+
port_number: int | None
|
174
|
+
#: Run ID whose command file can be used for this run (may be this run's ID).
|
175
|
+
commands_file_ID: int | None
|
176
|
+
|
177
|
+
|
178
|
+
class TaskMeta(TypedDict):
|
179
|
+
"""
|
180
|
+
Information about a task.
|
181
|
+
"""
|
182
|
+
|
183
|
+
#: The ID of the task.
|
184
|
+
id_: int
|
185
|
+
#: The index of the task in the workflow.
|
186
|
+
index: int
|
187
|
+
#: The elements in the task.
|
188
|
+
element_IDs: list[int]
|
189
|
+
|
190
|
+
|
191
|
+
class TemplateMeta(TypedDict): # FIXME: Incomplete, see WorkflowTemplate
|
192
|
+
"""
|
193
|
+
Metadata about a workflow template.
|
194
|
+
"""
|
195
|
+
|
196
|
+
#: Descriptors for loops.
|
197
|
+
loops: list[dict]
|
198
|
+
#: Descriptors for tasks.
|
199
|
+
tasks: list[dict]
|
200
|
+
|
201
|
+
|
202
|
+
class Metadata(TypedDict):
|
203
|
+
"""
|
204
|
+
Workflow metadata.
|
205
|
+
"""
|
206
|
+
|
207
|
+
#: Information about the store's creation.
|
208
|
+
creation_info: NotRequired[StoreCreationInfo]
|
209
|
+
#: Elements in the workflow.
|
210
|
+
elements: NotRequired[list[ElemMeta]]
|
211
|
+
#: Iterations in the workflow.
|
212
|
+
iters: NotRequired[list[IterMeta]]
|
213
|
+
#: Loops in the workflow.
|
214
|
+
loops: NotRequired[list[LoopDescriptor]]
|
215
|
+
#: The name of the workflow.
|
216
|
+
name: NotRequired[str]
|
217
|
+
#: The number of added tasks.
|
218
|
+
num_added_tasks: NotRequired[int]
|
219
|
+
#: The replacement workflow, if any.
|
220
|
+
replaced_workflow: NotRequired[str]
|
221
|
+
#: Element Action Runs in the workflow.
|
222
|
+
runs: NotRequired[list[RunMeta]]
|
223
|
+
#: Tasks in the workflow.
|
224
|
+
tasks: NotRequired[list[TaskMeta]]
|
225
|
+
#: The template that generated the workflow.
|
226
|
+
template: NotRequired[TemplateMeta]
|
227
|
+
#: Custom template components used.
|
228
|
+
template_components: NotRequired[dict[str, Any]]
|
229
|
+
#: Format for timestamps.
|
230
|
+
ts_fmt: NotRequired[str]
|
231
|
+
#: Format for timestamps used in naming.
|
232
|
+
ts_name_fmt: NotRequired[str]
|
233
|
+
|
234
|
+
|
235
|
+
class TypeLookup(TypedDict, total=False):
|
236
|
+
"""
|
237
|
+
Information for looking up the type of a parameter.
|
238
|
+
|
239
|
+
Note
|
240
|
+
----
|
241
|
+
Not a total typed dictionary.
|
242
|
+
"""
|
243
|
+
|
244
|
+
#: Tuples involving the parameter.
|
245
|
+
tuples: list[list[int]]
|
246
|
+
#: Sets involving the parameter.
|
247
|
+
sets: list[list[int]]
|
248
|
+
#: Arrays involving the parameter.
|
249
|
+
arrays: list[list[list[int] | int]]
|
250
|
+
#: Masked arrays involving the parameter.
|
251
|
+
masked_arrays: list[list[int | list[int]]]
|
252
|
+
|
253
|
+
|
254
|
+
class EncodedStoreParameter(TypedDict):
|
255
|
+
"""
|
256
|
+
The encoding of a :class:`StoreParameter`.
|
257
|
+
"""
|
258
|
+
|
259
|
+
#: The parameter data.
|
260
|
+
data: Any
|
261
|
+
#: Information for looking up the type.
|
262
|
+
type_lookup: TypeLookup
|
263
|
+
|
264
|
+
|
265
|
+
class PersistenceCache(
|
266
|
+
TypedDict, Generic[AnySTask, AnySElement, AnySElementIter, AnySEAR, AnySParameter]
|
267
|
+
):
|
268
|
+
"""
|
269
|
+
Cache used internally by the persistence engine.
|
270
|
+
"""
|
271
|
+
|
272
|
+
#: Tasks.
|
273
|
+
tasks: dict[int, AnySTask]
|
274
|
+
#: Elements.
|
275
|
+
elements: dict[int, AnySElement]
|
276
|
+
#: Element iterations.
|
277
|
+
element_iters: dict[int, AnySElementIter]
|
278
|
+
#: Element action runs.
|
279
|
+
EARs: dict[int, AnySEAR]
|
280
|
+
#: Parameter sources.
|
281
|
+
param_sources: dict[int, ParamSource]
|
282
|
+
#: Number of tasks.
|
283
|
+
num_tasks: int | None
|
284
|
+
#: Parameters.
|
285
|
+
parameters: dict[int, AnySParameter]
|
286
|
+
#: Number of element action runs.
|
287
|
+
num_EARs: int | None
|
288
|
+
#: Number of parameters.
|
289
|
+
num_params: int | None
|
290
|
+
|
291
|
+
|
292
|
+
class ZarrAttrsDict(TypedDict):
|
293
|
+
"""
|
294
|
+
Zarr workflow attributes descriptor.
|
295
|
+
"""
|
296
|
+
|
297
|
+
#: Workflow name.
|
298
|
+
name: str
|
299
|
+
#: Timestamp format.
|
300
|
+
ts_fmt: str
|
301
|
+
#: Timestamp format for names.
|
302
|
+
ts_name_fmt: str
|
303
|
+
#: Information about the creation of the workflow and persistent store.
|
304
|
+
creation_info: StoreCreationInfo
|
305
|
+
#: The template used to build the workflow.
|
306
|
+
template: TemplateMeta
|
307
|
+
#: Custom components used to build the workflow.
|
308
|
+
template_components: dict[str, Any]
|
309
|
+
#: Number of tasks added.
|
310
|
+
num_added_tasks: int
|
311
|
+
#: Tasks in the workflow.
|
312
|
+
tasks: list[dict[str, Any]]
|
313
|
+
#: Loops in the workflow.
|
314
|
+
loops: list[dict[str, Any]]
|
315
|
+
#: Submissions by the workflow.
|
316
|
+
submissions: list[Mapping[str, JSONed]]
|
317
|
+
#: Replacement workflow, if any.
|
318
|
+
replaced_workflow: NotRequired[str]
|
hpcflow/sdk/persistence/utils.py
CHANGED
@@ -2,12 +2,22 @@
|
|
2
2
|
Miscellaneous persistence-related helpers.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
5
6
|
from getpass import getpass
|
7
|
+
from typing import TYPE_CHECKING
|
6
8
|
|
7
9
|
from hpcflow.sdk.core.errors import WorkflowNotFoundError
|
8
10
|
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from typing import Callable, TypeVar
|
13
|
+
from fsspec import AbstractFileSystem # type: ignore
|
9
14
|
|
10
|
-
|
15
|
+
T = TypeVar("T")
|
16
|
+
|
17
|
+
|
18
|
+
def ask_pw_on_auth_exc(
|
19
|
+
f: Callable[..., T], *args, add_pw_to: str | None = None, **kwargs
|
20
|
+
) -> tuple[T, str | None]:
|
11
21
|
"""
|
12
22
|
Run the given function on the given arguments and add a password if the function
|
13
23
|
fails with an SSHException.
|
@@ -24,19 +34,14 @@ def ask_pw_on_auth_exc(f, *args, add_pw_to=None, **kwargs):
|
|
24
34
|
if not add_pw_to:
|
25
35
|
kwargs["password"] = pw
|
26
36
|
else:
|
27
|
-
kwargs[add_pw_to]["password"
|
37
|
+
kwargs[add_pw_to] = {**kwargs[add_pw_to], "password": pw}
|
28
38
|
|
29
39
|
out = f(*args, **kwargs)
|
30
40
|
|
31
|
-
if not add_pw_to:
|
32
|
-
del kwargs["password"]
|
33
|
-
else:
|
34
|
-
del kwargs[add_pw_to]["password"]
|
35
|
-
|
36
41
|
return out, pw
|
37
42
|
|
38
43
|
|
39
|
-
def infer_store(path: str, fs) -> str:
|
44
|
+
def infer_store(path: str, fs: AbstractFileSystem) -> str:
|
40
45
|
"""Identify the store type using the path and file system parsed by fsspec.
|
41
46
|
|
42
47
|
Parameters
|
@@ -63,8 +68,6 @@ def infer_store(path: str, fs) -> str:
|
|
63
68
|
elif fs.glob(f"{path}/metadata.json"):
|
64
69
|
store_fmt = "json"
|
65
70
|
else:
|
66
|
-
raise WorkflowNotFoundError(
|
67
|
-
f"Cannot infer a store format at path {path!r} with file system {fs!r}."
|
68
|
-
)
|
71
|
+
raise WorkflowNotFoundError(path, fs)
|
69
72
|
|
70
73
|
return store_fmt
|