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
@@ -1,19 +1,25 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
from dataclasses import dataclass
|
3
|
+
from pathlib import Path
|
2
4
|
import random
|
3
5
|
import string
|
4
6
|
import sys
|
5
7
|
from textwrap import dedent
|
8
|
+
from typing import ClassVar
|
6
9
|
|
7
10
|
import pytest
|
8
11
|
import requests
|
9
12
|
|
10
13
|
from hpcflow.app import app as hf
|
14
|
+
from hpcflow.sdk.core.errors import ParametersMetadataReadOnlyError
|
15
|
+
from hpcflow.sdk.typing import hydrate
|
11
16
|
from hpcflow.sdk.core.parameters import ParameterValue
|
12
17
|
|
13
18
|
|
14
19
|
@dataclass
|
20
|
+
@hydrate
|
15
21
|
class MyParameterP1(ParameterValue):
|
16
|
-
_typ = "p1_test"
|
22
|
+
_typ: ClassVar[str] = "p1_test"
|
17
23
|
a: int
|
18
24
|
|
19
25
|
def CLI_format(self):
|
@@ -22,7 +28,9 @@ class MyParameterP1(ParameterValue):
|
|
22
28
|
|
23
29
|
@pytest.mark.integration
|
24
30
|
@pytest.mark.parametrize("store", ["json", "zarr"])
|
25
|
-
def test_submission_with_specified_parameter_class_module(
|
31
|
+
def test_submission_with_specified_parameter_class_module(
|
32
|
+
null_config, tmp_path: Path, store: str
|
33
|
+
):
|
26
34
|
"""Test we can use a ParameterValue subclass that is defined separately from the main
|
27
35
|
code (i.e. not automatically imported on app init)."""
|
28
36
|
|
@@ -84,7 +92,7 @@ def test_submission_with_specified_parameter_class_module(null_config, tmp_path,
|
|
84
92
|
|
85
93
|
|
86
94
|
@pytest.mark.parametrize("store", ["json", "zarr"])
|
87
|
-
def test_unseen_parameter(null_config, tmp_path, store):
|
95
|
+
def test_unseen_parameter(null_config, tmp_path: Path, store: str):
|
88
96
|
"""Test we can generate a workflow that uses an unseen parameter type."""
|
89
97
|
|
90
98
|
random_str = "".join(random.choice(string.ascii_letters) for _ in range(10))
|
@@ -116,7 +124,7 @@ def test_unseen_parameter(null_config, tmp_path, store):
|
|
116
124
|
assert wk.tasks[0].elements[0].get(f"inputs.{p_type}") == 5
|
117
125
|
|
118
126
|
|
119
|
-
def test_iter(new_null_config, tmp_path):
|
127
|
+
def test_iter(new_null_config, tmp_path: Path):
|
120
128
|
values = [1, 2, 3]
|
121
129
|
wkt = hf.WorkflowTemplate(
|
122
130
|
name="test",
|
@@ -132,7 +140,7 @@ def test_iter(new_null_config, tmp_path):
|
|
132
140
|
assert param_p1_i.value == values[idx]
|
133
141
|
|
134
142
|
|
135
|
-
def test_slice(new_null_config, tmp_path):
|
143
|
+
def test_slice(new_null_config, tmp_path: Path):
|
136
144
|
values = [1, 2, 3]
|
137
145
|
wkt = hf.WorkflowTemplate(
|
138
146
|
name="test",
|
@@ -158,7 +166,7 @@ def test_slice(new_null_config, tmp_path):
|
|
158
166
|
"retrieving demo data from GitHub."
|
159
167
|
),
|
160
168
|
)
|
161
|
-
def test_demo_data_substitution_param_value_class_method(new_null_config, tmp_path):
|
169
|
+
def test_demo_data_substitution_param_value_class_method(new_null_config, tmp_path: Path):
|
162
170
|
yaml_str = dedent(
|
163
171
|
"""\
|
164
172
|
name: temp
|
@@ -189,7 +197,9 @@ def test_demo_data_substitution_param_value_class_method(new_null_config, tmp_pa
|
|
189
197
|
"retrieving demo data from GitHub."
|
190
198
|
),
|
191
199
|
)
|
192
|
-
def test_demo_data_substitution_value_sequence_class_method(
|
200
|
+
def test_demo_data_substitution_value_sequence_class_method(
|
201
|
+
new_null_config, tmp_path: Path
|
202
|
+
):
|
193
203
|
yaml_str = dedent(
|
194
204
|
"""\
|
195
205
|
name: temp
|
@@ -219,3 +229,15 @@ def test_demo_data_substitution_value_sequence_class_method(new_null_config, tmp
|
|
219
229
|
"9",
|
220
230
|
"10",
|
221
231
|
]
|
232
|
+
|
233
|
+
|
234
|
+
def test_json_store_parameters_metadata_cache_raises_on_modify(
|
235
|
+
null_config, tmp_path: Path
|
236
|
+
):
|
237
|
+
wk = hf.make_demo_workflow("workflow_1", path=tmp_path, store="json")
|
238
|
+
assert isinstance(wk, hf.Workflow)
|
239
|
+
num_params = len(wk.get_all_parameters())
|
240
|
+
with pytest.raises(ParametersMetadataReadOnlyError):
|
241
|
+
with wk._store.parameters_metadata_cache():
|
242
|
+
wk._add_unset_parameter_data(source={"type": "UNSET-FOR-THIS-TEST"})
|
243
|
+
assert len(wk.get_all_parameters()) == num_params
|
@@ -1,49 +1,65 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
from pathlib import Path
|
3
|
+
import sys
|
4
|
+
from typing import Any, Dict, List, Tuple
|
5
|
+
from typing import cast, TYPE_CHECKING
|
2
6
|
import numpy as np
|
3
|
-
import zarr
|
7
|
+
import zarr # type: ignore
|
4
8
|
import pytest
|
5
|
-
from hpcflow.sdk.core.test_utils import make_test_data_YAML_workflow
|
6
|
-
from hpcflow.sdk.persistence.
|
7
|
-
|
9
|
+
from hpcflow.sdk.core.test_utils import make_test_data_YAML_workflow, make_workflow
|
10
|
+
from hpcflow.sdk.persistence.json import (
|
11
|
+
JSONPersistentStore,
|
12
|
+
JsonStoreElement,
|
13
|
+
JsonStoreElementIter,
|
14
|
+
JsonStoreEAR,
|
15
|
+
)
|
16
|
+
from hpcflow.sdk.persistence.zarr import ZarrPersistentStore
|
8
17
|
|
9
18
|
from hpcflow.app import app as hf
|
10
19
|
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from hpcflow.sdk.persistence.zarr import ZarrPersistentStore
|
22
|
+
|
11
23
|
|
12
24
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
13
|
-
def test_store_pending_add_task(tmp_path):
|
25
|
+
def test_store_pending_add_task(tmp_path: Path):
|
14
26
|
"""Check expected pending state after adding a task."""
|
15
27
|
|
16
28
|
# make store: 0 tasks:
|
17
|
-
store = JSONPersistentStore.make_test_store_from_spec([], dir=tmp_path)
|
29
|
+
store = JSONPersistentStore.make_test_store_from_spec(hf, [], dir=tmp_path)
|
18
30
|
task_ID = store.add_task()
|
19
31
|
assert store._pending.add_tasks == {task_ID: []}
|
20
32
|
|
21
33
|
|
22
34
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
23
|
-
def test_store_pending_add_element(tmp_path):
|
35
|
+
def test_store_pending_add_element(tmp_path: Path):
|
24
36
|
"""Check expected pending state after adding an element."""
|
25
37
|
|
26
38
|
# make store: 1 task with 0 elements:
|
27
39
|
store = JSONPersistentStore.make_test_store_from_spec(app=hf, spec=[{}], dir=tmp_path)
|
28
40
|
elem_ID = store.add_element(task_ID=0)
|
29
41
|
assert store._pending.add_elements == {
|
30
|
-
elem_ID:
|
42
|
+
elem_ID: JsonStoreElement(
|
31
43
|
id_=elem_ID,
|
32
44
|
is_pending=True,
|
33
|
-
|
45
|
+
es_idx=0,
|
34
46
|
task_ID=0,
|
35
47
|
iteration_IDs=[],
|
48
|
+
index=0,
|
49
|
+
seq_idx={},
|
50
|
+
src_idx={},
|
36
51
|
)
|
37
52
|
} and store._pending.add_task_element_IDs == {0: [0]}
|
38
53
|
|
39
54
|
|
40
55
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
41
56
|
@pytest.mark.parametrize("elem_ID", [0, 1])
|
42
|
-
def test_store_pending_add_element_iter(tmp_path, elem_ID):
|
57
|
+
def test_store_pending_add_element_iter(tmp_path: Path, elem_ID: int):
|
43
58
|
"""Check expected pending state after adding an element iteration."""
|
44
59
|
|
45
60
|
# make store: 1 task with 2 elements and 0 iterations:
|
46
61
|
store = JSONPersistentStore.make_test_store_from_spec(
|
62
|
+
hf,
|
47
63
|
[{"elements": [{}, {}]}],
|
48
64
|
dir=tmp_path,
|
49
65
|
)
|
@@ -53,23 +69,25 @@ def test_store_pending_add_element_iter(tmp_path, elem_ID):
|
|
53
69
|
schema_parameters=[],
|
54
70
|
)
|
55
71
|
assert store._pending.add_elem_iters == {
|
56
|
-
iter_ID:
|
72
|
+
iter_ID: JsonStoreElementIter(
|
57
73
|
id_=iter_ID,
|
58
74
|
is_pending=True,
|
59
75
|
element_ID=elem_ID,
|
60
76
|
EAR_IDs={},
|
61
77
|
data_idx={},
|
62
78
|
schema_parameters=[],
|
79
|
+
EARs_initialised=False,
|
63
80
|
)
|
64
81
|
} and store._pending.add_elem_iter_IDs == {elem_ID: [iter_ID]}
|
65
82
|
|
66
83
|
|
67
84
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
68
|
-
def test_store_pending_add_EAR(tmp_path):
|
85
|
+
def test_store_pending_add_EAR(tmp_path: Path):
|
69
86
|
"""Check expected pending state after adding an EAR."""
|
70
87
|
|
71
88
|
# make store: 1 task with 1 element and 1 iteration:
|
72
89
|
store = JSONPersistentStore.make_test_store_from_spec(
|
90
|
+
hf,
|
73
91
|
[{"elements": [{"iterations": [{}]}]}],
|
74
92
|
dir=tmp_path,
|
75
93
|
)
|
@@ -81,7 +99,7 @@ def test_store_pending_add_EAR(tmp_path):
|
|
81
99
|
metadata={},
|
82
100
|
)
|
83
101
|
assert store._pending.add_EARs == {
|
84
|
-
EAR_ID:
|
102
|
+
EAR_ID: JsonStoreEAR(
|
85
103
|
id_=EAR_ID,
|
86
104
|
is_pending=True,
|
87
105
|
elem_iter_ID=0,
|
@@ -94,21 +112,21 @@ def test_store_pending_add_EAR(tmp_path):
|
|
94
112
|
|
95
113
|
|
96
114
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
97
|
-
def test_get_task_elements_task_is_pending(tmp_path):
|
115
|
+
def test_get_task_elements_task_is_pending(tmp_path: Path):
|
98
116
|
"""Check we get an empty list when getting all task elements of a pending task to
|
99
117
|
which no elements have been added."""
|
100
118
|
# make store: 0 tasks:
|
101
|
-
store = JSONPersistentStore.make_test_store_from_spec([], dir=tmp_path)
|
119
|
+
store = JSONPersistentStore.make_test_store_from_spec(hf, [], dir=tmp_path)
|
102
120
|
task_ID = store.add_task()
|
103
121
|
assert store.get_task_elements(task_ID, slice(0, None)) == []
|
104
122
|
|
105
123
|
|
106
124
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
107
|
-
def test_get_task_elements_single_element_is_pending(tmp_path):
|
125
|
+
def test_get_task_elements_single_element_is_pending(tmp_path: Path):
|
108
126
|
"""Check expected return when getting all task elements of a persistent task that has
|
109
127
|
a single pending element."""
|
110
128
|
# make store: 1 task
|
111
|
-
store = JSONPersistentStore.make_test_store_from_spec([{}], dir=tmp_path)
|
129
|
+
store = JSONPersistentStore.make_test_store_from_spec(hf, [{}], dir=tmp_path)
|
112
130
|
store.add_element(task_ID=0)
|
113
131
|
assert store.get_task_elements(0, slice(0, None)) == [
|
114
132
|
{
|
@@ -123,12 +141,12 @@ def test_get_task_elements_single_element_is_pending(tmp_path):
|
|
123
141
|
|
124
142
|
|
125
143
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
126
|
-
def test_get_task_elements_multi_element_one_pending(tmp_path):
|
144
|
+
def test_get_task_elements_multi_element_one_pending(tmp_path: Path):
|
127
145
|
"""Check expected return when getting all task elements of a persistent task that has
|
128
146
|
a persistent element and a pending element."""
|
129
147
|
# make store: 1 task with 1 element:
|
130
148
|
store = JSONPersistentStore.make_test_store_from_spec(
|
131
|
-
[{"elements": [{}]}], dir=tmp_path
|
149
|
+
hf, [{"elements": [{}]}], dir=tmp_path
|
132
150
|
)
|
133
151
|
store.add_element(task_ID=0)
|
134
152
|
assert store.get_task_elements(0, slice(0, None)) == [
|
@@ -152,12 +170,12 @@ def test_get_task_elements_multi_element_one_pending(tmp_path):
|
|
152
170
|
|
153
171
|
|
154
172
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
155
|
-
def test_get_task_elements_single_element_iter_pending(tmp_path):
|
173
|
+
def test_get_task_elements_single_element_iter_pending(tmp_path: Path):
|
156
174
|
"""Check expected return when getting all task elements of a persistent task that has
|
157
175
|
a persistent element with a pending iteration."""
|
158
176
|
# make store: 1 task with 1 element:
|
159
177
|
store = JSONPersistentStore.make_test_store_from_spec(
|
160
|
-
[{"elements": [{}]}], dir=tmp_path
|
178
|
+
hf, [{"elements": [{}]}], dir=tmp_path
|
161
179
|
)
|
162
180
|
store.add_element_iteration(element_ID=0, data_idx={}, schema_parameters=[])
|
163
181
|
assert store.get_task_elements(0, slice(0, None)) == [
|
@@ -183,12 +201,12 @@ def test_get_task_elements_single_element_iter_pending(tmp_path):
|
|
183
201
|
|
184
202
|
|
185
203
|
@pytest.mark.skip("need to refactor `make_test_store_from_spec`")
|
186
|
-
def test_get_task_elements_single_element_iter_EAR_pending(tmp_path):
|
204
|
+
def test_get_task_elements_single_element_iter_EAR_pending(tmp_path: Path):
|
187
205
|
"""Check expected return when getting all task elements of a persistent task that has
|
188
206
|
a persistent element with a persistent iteration and a pending EAR"""
|
189
207
|
# make store: 1 task with 1 element with 1 iteration:
|
190
208
|
store = JSONPersistentStore.make_test_store_from_spec(
|
191
|
-
[{"elements": [{"iterations": [{}]}]}], dir=tmp_path
|
209
|
+
hf, [{"elements": [{"iterations": [{}]}]}], dir=tmp_path
|
192
210
|
)
|
193
211
|
store.add_EAR(elem_iter_ID=0, action_idx=0, commands_idx=[], data_idx={}, metadata={})
|
194
212
|
assert store.get_task_elements(0, slice(0, None)) == [
|
@@ -225,7 +243,7 @@ def test_get_task_elements_single_element_iter_EAR_pending(tmp_path):
|
|
225
243
|
]
|
226
244
|
|
227
245
|
|
228
|
-
def test_make_zarr_store_zstd_compressor(null_config, tmp_path):
|
246
|
+
def test_make_zarr_store_zstd_compressor(null_config, tmp_path: Path):
|
229
247
|
wk = make_test_data_YAML_workflow(
|
230
248
|
workflow_name="workflow_1.yaml",
|
231
249
|
path=tmp_path,
|
@@ -234,7 +252,7 @@ def test_make_zarr_store_zstd_compressor(null_config, tmp_path):
|
|
234
252
|
)
|
235
253
|
|
236
254
|
|
237
|
-
def test_make_zarr_store_no_compressor(null_config, tmp_path):
|
255
|
+
def test_make_zarr_store_no_compressor(null_config, tmp_path: Path):
|
238
256
|
wk = make_test_data_YAML_workflow(
|
239
257
|
workflow_name="workflow_1.yaml",
|
240
258
|
path=tmp_path,
|
@@ -244,7 +262,10 @@ def test_make_zarr_store_no_compressor(null_config, tmp_path):
|
|
244
262
|
|
245
263
|
|
246
264
|
@pytest.mark.integration
|
247
|
-
|
265
|
+
@pytest.mark.skipif(
|
266
|
+
sys.version_info < (3, 9), reason="Python 3.8 support is being removed anyway."
|
267
|
+
)
|
268
|
+
def test_zarr_rechunk_data_equivalent(null_config, tmp_path: Path):
|
248
269
|
t1 = hf.Task(
|
249
270
|
schema=hf.task_schemas.test_t1_conditional_OS,
|
250
271
|
inputs={"p1": 101},
|
@@ -259,13 +280,13 @@ def test_zarr_rechunk_data_equivalent(null_config, tmp_path):
|
|
259
280
|
wk.submit(wait=True, status=False, add_to_known=False)
|
260
281
|
wk.rechunk_runs(backup=True, status=False, chunk_size=None) # None -> one chunk
|
261
282
|
|
262
|
-
arr = wk._store._get_EARs_arr()
|
283
|
+
arr = cast("ZarrPersistentStore", wk._store)._get_EARs_arr()
|
263
284
|
assert arr.chunks == arr.shape
|
264
285
|
|
265
|
-
bak_path = (Path(
|
286
|
+
bak_path = (Path(arr.store.path) / arr.path).with_suffix(".bak")
|
266
287
|
arr_bak = zarr.open(bak_path)
|
267
288
|
|
268
|
-
assert arr_bak.chunks == (1,)
|
289
|
+
assert arr_bak.chunks == (1, 1) # runs array is 2D
|
269
290
|
|
270
291
|
# check backup and new runs data are equal:
|
271
292
|
assert np.all(arr[:] == arr_bak[:])
|
@@ -275,7 +296,10 @@ def test_zarr_rechunk_data_equivalent(null_config, tmp_path):
|
|
275
296
|
|
276
297
|
|
277
298
|
@pytest.mark.integration
|
278
|
-
|
299
|
+
@pytest.mark.skipif(
|
300
|
+
sys.version_info < (3, 9), reason="Python 3.8 support is being removed anyway."
|
301
|
+
)
|
302
|
+
def test_zarr_rechunk_data_equivalent_custom_chunk_size(null_config, tmp_path: Path):
|
279
303
|
t1 = hf.Task(
|
280
304
|
schema=hf.task_schemas.test_t1_conditional_OS,
|
281
305
|
inputs={"p1": 101},
|
@@ -290,20 +314,20 @@ def test_zarr_rechunk_data_equivalent_custom_chunk_size(null_config, tmp_path):
|
|
290
314
|
wk.submit(wait=True, status=False, add_to_known=False)
|
291
315
|
wk.rechunk_runs(backup=True, status=False, chunk_size=2)
|
292
316
|
|
293
|
-
arr = wk._store._get_EARs_arr()
|
294
|
-
assert arr.chunks == (2,)
|
317
|
+
arr = cast("ZarrPersistentStore", wk._store)._get_EARs_arr()
|
318
|
+
assert arr.chunks == (2, 2) # runs array is 2D
|
295
319
|
|
296
|
-
bak_path = (Path(
|
320
|
+
bak_path = (Path(arr.store.path) / arr.path).with_suffix(".bak")
|
297
321
|
arr_bak = zarr.open(bak_path)
|
298
322
|
|
299
|
-
assert arr_bak.chunks == (1,)
|
323
|
+
assert arr_bak.chunks == (1, 1) # runs array is 2D
|
300
324
|
|
301
325
|
# check backup and new runs data are equal:
|
302
326
|
assert np.all(arr[:] == arr_bak[:])
|
303
327
|
|
304
328
|
|
305
329
|
@pytest.mark.integration
|
306
|
-
def test_zarr_rechunk_data_no_backup_load_runs(null_config, tmp_path):
|
330
|
+
def test_zarr_rechunk_data_no_backup_load_runs(null_config, tmp_path: Path):
|
307
331
|
t1 = hf.Task(
|
308
332
|
schema=hf.task_schemas.test_t1_conditional_OS,
|
309
333
|
inputs={"p1": 101},
|
@@ -318,9 +342,9 @@ def test_zarr_rechunk_data_no_backup_load_runs(null_config, tmp_path):
|
|
318
342
|
wk.submit(wait=True, status=False, add_to_known=False)
|
319
343
|
wk.rechunk_runs(backup=False, status=False)
|
320
344
|
|
321
|
-
arr = wk._store._get_EARs_arr()
|
345
|
+
arr = cast("ZarrPersistentStore", wk._store)._get_EARs_arr()
|
322
346
|
|
323
|
-
bak_path = (Path(
|
347
|
+
bak_path = (Path(arr.store.path) / arr.path).with_suffix(".bak")
|
324
348
|
assert not bak_path.is_file()
|
325
349
|
|
326
350
|
# check we can load runs:
|
@@ -331,7 +355,7 @@ def test_zarr_rechunk_data_no_backup_load_runs(null_config, tmp_path):
|
|
331
355
|
|
332
356
|
|
333
357
|
@pytest.mark.integration
|
334
|
-
def test_zarr_rechunk_data_no_backup_load_parameter_base(null_config, tmp_path):
|
358
|
+
def test_zarr_rechunk_data_no_backup_load_parameter_base(null_config, tmp_path: Path):
|
335
359
|
t1 = hf.Task(
|
336
360
|
schema=hf.task_schemas.test_t1_conditional_OS,
|
337
361
|
inputs={"p1": 101},
|
@@ -344,15 +368,186 @@ def test_zarr_rechunk_data_no_backup_load_parameter_base(null_config, tmp_path):
|
|
344
368
|
path=tmp_path,
|
345
369
|
)
|
346
370
|
wk.submit(wait=True, status=False, add_to_known=False)
|
371
|
+
|
372
|
+
params_old = wk.get_all_parameter_data()
|
347
373
|
wk.rechunk_parameter_base(backup=False, status=False)
|
348
374
|
|
349
|
-
|
375
|
+
wk = wk.reload()
|
376
|
+
params_new = wk.get_all_parameter_data()
|
377
|
+
assert params_new == params_old
|
378
|
+
|
379
|
+
arr = cast("ZarrPersistentStore", wk._store)._get_parameter_base_array()
|
350
380
|
|
351
|
-
bak_path = (Path(
|
381
|
+
bak_path = (Path(arr.store.path) / arr.path).with_suffix(".bak")
|
352
382
|
assert not bak_path.is_file()
|
353
383
|
|
354
384
|
# check we can load parameters:
|
355
|
-
params = wk.get_all_parameters()
|
356
385
|
param_IDs = []
|
357
|
-
for i in
|
386
|
+
for i in wk.get_all_parameters():
|
358
387
|
param_IDs.append(i.id_)
|
388
|
+
|
389
|
+
|
390
|
+
def test_get_parameter_sources_duplicate_ids(null_config, tmp_path):
|
391
|
+
wk = make_workflow(
|
392
|
+
schemas_spec=[[{"p1": None}, ("p1",), "t1"]],
|
393
|
+
local_inputs={0: ("p1",)},
|
394
|
+
path=tmp_path,
|
395
|
+
)
|
396
|
+
id_lst = [0, 1, 1, 2, 0]
|
397
|
+
src = wk._store.get_parameter_sources(id_lst)
|
398
|
+
assert len(src) == len(id_lst)
|
399
|
+
assert src[0] == src[4]
|
400
|
+
assert src[1] == src[2]
|
401
|
+
|
402
|
+
|
403
|
+
def _transform_jobscript_dependencies_to_encodable(
|
404
|
+
deps: dict[tuple[int, int], dict[tuple[int, int], dict[str, Any]]],
|
405
|
+
) -> dict[str, list[dict[str, Any]]]:
|
406
|
+
"""Transform a dict of jobscript dependencies written in a more testing-friendly/
|
407
|
+
convenient format into the format expected by the method
|
408
|
+
`ZarrPersistentStore._encode_jobscript_block_dependencies`.
|
409
|
+
|
410
|
+
"""
|
411
|
+
max_js_idx = max(i[0] for i in deps)
|
412
|
+
sub_js: dict[str, list[dict[str, Any]]] = {
|
413
|
+
"jobscripts": [
|
414
|
+
{"blocks": [], "index": js_idx} for js_idx in range(max_js_idx + 1)
|
415
|
+
]
|
416
|
+
}
|
417
|
+
for (js_idx, blk_idx), deps_i in deps.items():
|
418
|
+
sub_js["jobscripts"][js_idx]["blocks"].append(
|
419
|
+
{
|
420
|
+
"dependencies": [[[k[0], k[1]], v] for k, v in deps_i.items()],
|
421
|
+
"index": blk_idx,
|
422
|
+
}
|
423
|
+
)
|
424
|
+
return sub_js
|
425
|
+
|
426
|
+
|
427
|
+
def test_zarr_encode_jobscript_block_dependencies_element_mapping_array_non_array_equivalence():
|
428
|
+
deps_1 = {
|
429
|
+
(0, 0): {},
|
430
|
+
(1, 0): {(0, 0): {"js_element_mapping": {0: [0]}, "is_array": True}},
|
431
|
+
}
|
432
|
+
deps_2 = {
|
433
|
+
(0, 0): {},
|
434
|
+
(1, 0): {(0, 0): {"js_element_mapping": {0: np.array([0])}, "is_array": True}},
|
435
|
+
}
|
436
|
+
deps_1 = _transform_jobscript_dependencies_to_encodable(deps_1)
|
437
|
+
deps_2 = _transform_jobscript_dependencies_to_encodable(deps_2)
|
438
|
+
arr_1 = ZarrPersistentStore._encode_jobscript_block_dependencies(deps_1)
|
439
|
+
arr_2 = ZarrPersistentStore._encode_jobscript_block_dependencies(deps_2)
|
440
|
+
assert np.array_equal(arr_1, arr_2)
|
441
|
+
|
442
|
+
|
443
|
+
def test_zarr_encode_decode_jobscript_block_dependencies():
|
444
|
+
|
445
|
+
deps = {
|
446
|
+
(0, 0): {},
|
447
|
+
(1, 0): {
|
448
|
+
(0, 0): {
|
449
|
+
"js_element_mapping": {0: [0], 1: [1]},
|
450
|
+
"is_array": True,
|
451
|
+
}
|
452
|
+
},
|
453
|
+
(2, 0): {
|
454
|
+
(1, 0): {
|
455
|
+
"js_element_mapping": {0: [0, 1], 1: [0, 1]},
|
456
|
+
"is_array": False,
|
457
|
+
}
|
458
|
+
},
|
459
|
+
(2, 1): {
|
460
|
+
(0, 0): {"js_element_mapping": {0: [0, 1]}, "is_array": False},
|
461
|
+
(2, 0): {"js_element_mapping": {0: [0, 1]}, "is_array": False},
|
462
|
+
},
|
463
|
+
}
|
464
|
+
deps_t = _transform_jobscript_dependencies_to_encodable(deps)
|
465
|
+
arr = ZarrPersistentStore._encode_jobscript_block_dependencies(deps_t)
|
466
|
+
assert np.array_equal(
|
467
|
+
arr,
|
468
|
+
np.array(
|
469
|
+
[
|
470
|
+
2,
|
471
|
+
0,
|
472
|
+
0,
|
473
|
+
12,
|
474
|
+
1,
|
475
|
+
0,
|
476
|
+
9,
|
477
|
+
0,
|
478
|
+
0,
|
479
|
+
1,
|
480
|
+
2,
|
481
|
+
0,
|
482
|
+
0,
|
483
|
+
2,
|
484
|
+
1,
|
485
|
+
1,
|
486
|
+
14,
|
487
|
+
2,
|
488
|
+
0,
|
489
|
+
11,
|
490
|
+
1,
|
491
|
+
0,
|
492
|
+
0,
|
493
|
+
3,
|
494
|
+
0,
|
495
|
+
0,
|
496
|
+
1,
|
497
|
+
3,
|
498
|
+
1,
|
499
|
+
0,
|
500
|
+
1,
|
501
|
+
18,
|
502
|
+
2,
|
503
|
+
1,
|
504
|
+
7,
|
505
|
+
0,
|
506
|
+
0,
|
507
|
+
0,
|
508
|
+
3,
|
509
|
+
0,
|
510
|
+
0,
|
511
|
+
1,
|
512
|
+
7,
|
513
|
+
2,
|
514
|
+
0,
|
515
|
+
0,
|
516
|
+
3,
|
517
|
+
0,
|
518
|
+
0,
|
519
|
+
1,
|
520
|
+
]
|
521
|
+
),
|
522
|
+
)
|
523
|
+
deps_rt = ZarrPersistentStore._decode_jobscript_block_dependencies(arr)
|
524
|
+
assert deps_rt == deps
|
525
|
+
|
526
|
+
|
527
|
+
def test_zarr_encode_decode_jobscript_block_dependencies_large_many_to_one():
|
528
|
+
deps = {
|
529
|
+
(0, 0): {},
|
530
|
+
(1, 0): {
|
531
|
+
(0, 0): {"js_element_mapping": {0: list(range(1_000_000))}, "is_array": False}
|
532
|
+
},
|
533
|
+
}
|
534
|
+
deps_t = _transform_jobscript_dependencies_to_encodable(deps)
|
535
|
+
arr = ZarrPersistentStore._encode_jobscript_block_dependencies(deps_t)
|
536
|
+
deps_rt = ZarrPersistentStore._decode_jobscript_block_dependencies(arr)
|
537
|
+
assert deps_rt == deps
|
538
|
+
|
539
|
+
|
540
|
+
def test_zarr_encode_decode_jobscript_block_dependencies_large_one_to_one():
|
541
|
+
deps = {
|
542
|
+
(0, 0): {},
|
543
|
+
(1, 0): {
|
544
|
+
(0, 0): {
|
545
|
+
"js_element_mapping": {i: [i] for i in range(1_000_000)},
|
546
|
+
"is_array": False,
|
547
|
+
}
|
548
|
+
},
|
549
|
+
}
|
550
|
+
deps_t = _transform_jobscript_dependencies_to_encodable(deps)
|
551
|
+
arr = ZarrPersistentStore._encode_jobscript_block_dependencies(deps_t)
|
552
|
+
deps_rt = ZarrPersistentStore._decode_jobscript_block_dependencies(arr)
|
553
|
+
assert deps_rt == deps
|