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,4 +1,5 @@
|
|
1
|
-
import
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
2
3
|
import numpy as np
|
3
4
|
import pytest
|
4
5
|
from hpcflow.app import app as hf
|
@@ -9,7 +10,7 @@ from hpcflow.sdk.core.test_utils import (
|
|
9
10
|
)
|
10
11
|
|
11
12
|
|
12
|
-
def test_get_command_line(null_config, tmp_path):
|
13
|
+
def test_get_command_line(null_config, tmp_path: Path):
|
13
14
|
s1 = hf.TaskSchema(
|
14
15
|
objective="t1",
|
15
16
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
|
@@ -40,7 +41,9 @@ def test_get_command_line(null_config, tmp_path):
|
|
40
41
|
|
41
42
|
|
42
43
|
@pytest.mark.parametrize("shell_args", [("powershell", "nt"), ("bash", "posix")])
|
43
|
-
def test_get_command_line_with_stdout(
|
44
|
+
def test_get_command_line_with_stdout(
|
45
|
+
null_config, tmp_path: Path, shell_args: tuple[str, str]
|
46
|
+
):
|
44
47
|
s1 = hf.TaskSchema(
|
45
48
|
objective="t1",
|
46
49
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
|
@@ -76,7 +79,7 @@ def test_get_command_line_with_stdout(null_config, tmp_path, shell_args):
|
|
76
79
|
assert cmd_str == f"parameter_p2=`Write-Output ({p1_value} + 100)`"
|
77
80
|
|
78
81
|
|
79
|
-
def test_get_command_line_single_labelled_input(null_config, tmp_path):
|
82
|
+
def test_get_command_line_single_labelled_input(null_config, tmp_path: Path):
|
80
83
|
s1 = hf.TaskSchema(
|
81
84
|
objective="t1",
|
82
85
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"), labels={"one": {}})],
|
@@ -108,7 +111,7 @@ def test_get_command_line_single_labelled_input(null_config, tmp_path):
|
|
108
111
|
assert cmd_str == f"Write-Output ({p1_value} + 100)"
|
109
112
|
|
110
113
|
|
111
|
-
def test_get_command_line_multiple_labelled_input(null_config, tmp_path):
|
114
|
+
def test_get_command_line_multiple_labelled_input(null_config, tmp_path: Path):
|
112
115
|
s1 = hf.TaskSchema(
|
113
116
|
objective="t1",
|
114
117
|
inputs=[
|
@@ -151,7 +154,7 @@ def test_get_command_line_multiple_labelled_input(null_config, tmp_path):
|
|
151
154
|
assert cmd_str == f"Write-Output ({p1_one_value} + {p1_two_value} + 100)"
|
152
155
|
|
153
156
|
|
154
|
-
def test_get_command_line_sub_parameter(null_config, tmp_path):
|
157
|
+
def test_get_command_line_sub_parameter(null_config, tmp_path: Path):
|
155
158
|
s1 = hf.TaskSchema(
|
156
159
|
objective="t1",
|
157
160
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
|
@@ -181,7 +184,7 @@ def test_get_command_line_sub_parameter(null_config, tmp_path):
|
|
181
184
|
assert cmd_str == f"Write-Output ({p1_value['a']} + 100)"
|
182
185
|
|
183
186
|
|
184
|
-
def test_get_command_line_sum(null_config, tmp_path):
|
187
|
+
def test_get_command_line_sum(null_config, tmp_path: Path):
|
185
188
|
s1 = hf.TaskSchema(
|
186
189
|
objective="t1",
|
187
190
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
|
@@ -208,7 +211,7 @@ def test_get_command_line_sum(null_config, tmp_path):
|
|
208
211
|
assert cmd_str == f"Write-Output ({sum(p1_value)} + 100)"
|
209
212
|
|
210
213
|
|
211
|
-
def test_get_command_line_join(null_config, tmp_path):
|
214
|
+
def test_get_command_line_join(null_config, tmp_path: Path):
|
212
215
|
delim = ","
|
213
216
|
s1 = hf.TaskSchema(
|
214
217
|
objective="t1",
|
@@ -238,7 +241,7 @@ def test_get_command_line_join(null_config, tmp_path):
|
|
238
241
|
assert cmd_str == f"Write-Output ({delim.join(str(i) for i in p1_value)} + 100)"
|
239
242
|
|
240
243
|
|
241
|
-
def test_get_command_line_sum_sub_data(null_config, tmp_path):
|
244
|
+
def test_get_command_line_sum_sub_data(null_config, tmp_path: Path):
|
242
245
|
s1 = hf.TaskSchema(
|
243
246
|
objective="t1",
|
244
247
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
|
@@ -295,7 +298,7 @@ def test_get_command_line_join_sub_data(null_config, tmp_path):
|
|
295
298
|
assert cmd_str == f"Write-Output ({delim.join(str(i) for i in p1_value['a'])} + 100)"
|
296
299
|
|
297
300
|
|
298
|
-
def test_get_command_line_parameter_value(null_config, tmp_path):
|
301
|
+
def test_get_command_line_parameter_value(null_config, tmp_path: Path):
|
299
302
|
s1 = hf.TaskSchema(
|
300
303
|
objective="t1",
|
301
304
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1c"))],
|
@@ -325,7 +328,7 @@ def test_get_command_line_parameter_value(null_config, tmp_path):
|
|
325
328
|
assert cmd_str == f"Write-Output ({p1_value.a} + 100)"
|
326
329
|
|
327
330
|
|
328
|
-
def test_get_command_line_parameter_value_join(null_config, tmp_path):
|
331
|
+
def test_get_command_line_parameter_value_join(null_config, tmp_path: Path):
|
329
332
|
delim = ","
|
330
333
|
cmd = (
|
331
334
|
f"Write-Output "
|
@@ -349,14 +352,16 @@ def test_get_command_line_parameter_value_join(null_config, tmp_path):
|
|
349
352
|
path=tmp_path,
|
350
353
|
template_name="wk0",
|
351
354
|
)
|
352
|
-
|
353
|
-
|
355
|
+
t1 = wk.tasks.t1
|
356
|
+
assert isinstance(t1, hf.WorkflowTask)
|
357
|
+
run = t1.elements[0].iterations[0].action_runs[0]
|
358
|
+
command = run.action.commands[0]
|
354
359
|
shell = ALL_SHELLS["powershell"]["nt"]()
|
355
|
-
cmd_str, _ =
|
360
|
+
cmd_str, _ = command.get_command_line(EAR=run, shell=shell, env=run.get_environment())
|
356
361
|
assert cmd_str == f"Write-Output 4,4,4,4"
|
357
362
|
|
358
363
|
|
359
|
-
def test_get_command_line_parameter_value_custom_method(null_config, tmp_path):
|
364
|
+
def test_get_command_line_parameter_value_custom_method(null_config, tmp_path: Path):
|
360
365
|
s1 = hf.TaskSchema(
|
361
366
|
objective="t1",
|
362
367
|
inputs=[hf.SchemaInput(parameter=hf.Parameter("p1c"))],
|
@@ -386,7 +391,9 @@ def test_get_command_line_parameter_value_custom_method(null_config, tmp_path):
|
|
386
391
|
assert cmd_str == f"Write-Output ({p1_value.a + 4} + 100)"
|
387
392
|
|
388
393
|
|
389
|
-
def test_get_command_line_parameter_value_custom_method_with_args(
|
394
|
+
def test_get_command_line_parameter_value_custom_method_with_args(
|
395
|
+
null_config, tmp_path: Path
|
396
|
+
):
|
390
397
|
add_val = 35
|
391
398
|
s1 = hf.TaskSchema(
|
392
399
|
objective="t1",
|
@@ -409,7 +416,9 @@ def test_get_command_line_parameter_value_custom_method_with_args(null_config, t
|
|
409
416
|
template_name="wk0",
|
410
417
|
overwrite=True,
|
411
418
|
)
|
412
|
-
|
419
|
+
t1 = wk.tasks.t1
|
420
|
+
assert isinstance(t1, hf.WorkflowTask)
|
421
|
+
run = t1.elements[0].iterations[0].action_runs[0]
|
413
422
|
cmd = run.action.commands[0]
|
414
423
|
shell = ALL_SHELLS["powershell"]["nt"]()
|
415
424
|
cmd_str, _ = cmd.get_command_line(EAR=run, shell=shell, env=run.get_environment())
|
@@ -418,7 +427,7 @@ def test_get_command_line_parameter_value_custom_method_with_args(null_config, t
|
|
418
427
|
|
419
428
|
|
420
429
|
def test_get_command_line_parameter_value_custom_method_with_two_args(
|
421
|
-
null_config, tmp_path
|
430
|
+
null_config, tmp_path: Path
|
422
431
|
):
|
423
432
|
add_val = 35
|
424
433
|
sub_val = 10
|
@@ -439,15 +448,17 @@ def test_get_command_line_parameter_value_custom_method_with_two_args(
|
|
439
448
|
template_name="wk0",
|
440
449
|
overwrite=True,
|
441
450
|
)
|
442
|
-
|
443
|
-
|
451
|
+
t1 = wk.tasks.t1
|
452
|
+
assert isinstance(t1, hf.WorkflowTask)
|
453
|
+
run = t1.elements[0].iterations[0].action_runs[0]
|
454
|
+
command = run.action.commands[0]
|
444
455
|
shell = ALL_SHELLS["powershell"]["nt"]()
|
445
|
-
cmd_str, _ =
|
456
|
+
cmd_str, _ = command.get_command_line(EAR=run, shell=shell, env=run.get_environment())
|
446
457
|
|
447
458
|
assert cmd_str == f"Write-Output ({p1_value.a + add_val - sub_val} + 100)"
|
448
459
|
|
449
460
|
|
450
|
-
def test_get_command_line_parameter_value_sub_object(null_config, tmp_path):
|
461
|
+
def test_get_command_line_parameter_value_sub_object(null_config, tmp_path: Path):
|
451
462
|
cmd = f"Write-Output (<<parameter:p1c.sub_param>> + 100)"
|
452
463
|
s1 = hf.TaskSchema(
|
453
464
|
objective="t1",
|
@@ -462,15 +473,18 @@ def test_get_command_line_parameter_value_sub_object(null_config, tmp_path):
|
|
462
473
|
template_name="wk0",
|
463
474
|
overwrite=True,
|
464
475
|
)
|
465
|
-
|
466
|
-
|
476
|
+
t1 = wk.tasks.t1
|
477
|
+
assert isinstance(t1, hf.WorkflowTask)
|
478
|
+
run = t1.elements[0].iterations[0].action_runs[0]
|
479
|
+
command = run.action.commands[0]
|
467
480
|
shell = ALL_SHELLS["powershell"]["nt"]()
|
468
|
-
cmd_str, _ =
|
481
|
+
cmd_str, _ = command.get_command_line(EAR=run, shell=shell, env=run.get_environment())
|
469
482
|
|
483
|
+
assert p1_value.sub_param is not None
|
470
484
|
assert cmd_str == f"Write-Output ({p1_value.sub_param.e} + 100)"
|
471
485
|
|
472
486
|
|
473
|
-
def test_get_command_line_parameter_value_sub_object_attr(null_config, tmp_path):
|
487
|
+
def test_get_command_line_parameter_value_sub_object_attr(null_config, tmp_path: Path):
|
474
488
|
cmd = f"Write-Output (" f"<<parameter:p1c.sub_param.e>> + 100)"
|
475
489
|
s1 = hf.TaskSchema(
|
476
490
|
objective="t1",
|
@@ -485,49 +499,52 @@ def test_get_command_line_parameter_value_sub_object_attr(null_config, tmp_path)
|
|
485
499
|
template_name="wk0",
|
486
500
|
overwrite=True,
|
487
501
|
)
|
488
|
-
|
489
|
-
|
502
|
+
t1 = wk.tasks.t1
|
503
|
+
assert isinstance(t1, hf.WorkflowTask)
|
504
|
+
run = t1.elements[0].iterations[0].action_runs[0]
|
505
|
+
command = run.action.commands[0]
|
490
506
|
shell = ALL_SHELLS["powershell"]["nt"]()
|
491
|
-
cmd_str, _ =
|
507
|
+
cmd_str, _ = command.get_command_line(EAR=run, shell=shell, env=run.get_environment())
|
492
508
|
|
509
|
+
assert p1_value.sub_param is not None
|
493
510
|
assert cmd_str == f"Write-Output ({p1_value.sub_param.e} + 100)"
|
494
511
|
|
495
512
|
|
496
|
-
def test_process_std_stream_int(null_config):
|
513
|
+
def test_process_std_stream_int(null_config) -> None:
|
497
514
|
cmd = hf.Command(command="", stdout="<<int(parameter:p2)>>")
|
498
515
|
assert cmd.process_std_stream(name="p2", value="101", stderr=False) == 101
|
499
516
|
|
500
517
|
|
501
|
-
def test_process_std_stream_stderr_int(null_config):
|
518
|
+
def test_process_std_stream_stderr_int(null_config) -> None:
|
502
519
|
cmd = hf.Command(command="", stderr="<<int(parameter:p2)>>")
|
503
520
|
assert cmd.process_std_stream(name="p2", value="101", stderr=True) == 101
|
504
521
|
|
505
522
|
|
506
|
-
def test_process_std_stream_float(null_config):
|
523
|
+
def test_process_std_stream_float(null_config) -> None:
|
507
524
|
cmd = hf.Command(command="", stdout="<<float(parameter:p2)>>")
|
508
525
|
assert cmd.process_std_stream(name="p2", value="3.1415", stderr=False) == 3.1415
|
509
526
|
|
510
527
|
|
511
|
-
def test_process_std_stream_bool_true(null_config):
|
528
|
+
def test_process_std_stream_bool_true(null_config) -> None:
|
512
529
|
cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
|
513
530
|
for value in ("true", "True", "1"):
|
514
531
|
assert cmd.process_std_stream(name="p2", value=value, stderr=False) == True
|
515
532
|
|
516
533
|
|
517
|
-
def test_process_std_stream_bool_false(null_config):
|
534
|
+
def test_process_std_stream_bool_false(null_config) -> None:
|
518
535
|
cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
|
519
536
|
for value in ("false", "False", "0"):
|
520
537
|
assert cmd.process_std_stream(name="p2", value=value, stderr=False) == False
|
521
538
|
|
522
539
|
|
523
|
-
def test_process_std_stream_bool_raise(null_config):
|
540
|
+
def test_process_std_stream_bool_raise(null_config) -> None:
|
524
541
|
cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
|
525
542
|
for value in ("hi", "120", "-1"):
|
526
543
|
with pytest.raises(ValueError):
|
527
544
|
cmd.process_std_stream(name="p2", value=value, stderr=False)
|
528
545
|
|
529
546
|
|
530
|
-
def test_process_std_stream_list(null_config):
|
547
|
+
def test_process_std_stream_list(null_config) -> None:
|
531
548
|
cmd = hf.Command(command="", stdout="<<list(parameter:p2)>>")
|
532
549
|
assert cmd.process_std_stream(name="p2", value="1 2 3", stderr=False) == [
|
533
550
|
"1",
|
@@ -536,12 +553,12 @@ def test_process_std_stream_list(null_config):
|
|
536
553
|
]
|
537
554
|
|
538
555
|
|
539
|
-
def test_process_std_stream_list_int(null_config):
|
556
|
+
def test_process_std_stream_list_int(null_config) -> None:
|
540
557
|
cmd = hf.Command(command="", stdout="<<list[item_type=int](parameter:p2)>>")
|
541
558
|
assert cmd.process_std_stream(name="p2", value="1 2 3", stderr=False) == [1, 2, 3]
|
542
559
|
|
543
560
|
|
544
|
-
def test_process_std_stream_list_delim(null_config):
|
561
|
+
def test_process_std_stream_list_delim(null_config) -> None:
|
545
562
|
cmd = hf.Command(command="", stdout='<<list[delim=","](parameter:p2)>>')
|
546
563
|
assert cmd.process_std_stream(name="p2", value="1,2,3", stderr=False) == [
|
547
564
|
"1",
|
@@ -550,14 +567,14 @@ def test_process_std_stream_list_delim(null_config):
|
|
550
567
|
]
|
551
568
|
|
552
569
|
|
553
|
-
def test_process_std_stream_list_int_delim(null_config):
|
570
|
+
def test_process_std_stream_list_int_delim(null_config) -> None:
|
554
571
|
cmd = hf.Command(
|
555
572
|
command="", stdout='<<list[item_type=int, delim=","](parameter:p2)>>'
|
556
573
|
)
|
557
574
|
assert cmd.process_std_stream(name="p2", value="1,2,3", stderr=False) == [1, 2, 3]
|
558
575
|
|
559
576
|
|
560
|
-
def test_process_std_stream_list_float_delim_colon(null_config):
|
577
|
+
def test_process_std_stream_list_float_delim_colon(null_config) -> None:
|
561
578
|
cmd = hf.Command(
|
562
579
|
command="", stdout='<<list[item_type=float, delim=":"](parameter:p2)>>'
|
563
580
|
)
|
@@ -568,7 +585,7 @@ def test_process_std_stream_list_float_delim_colon(null_config):
|
|
568
585
|
]
|
569
586
|
|
570
587
|
|
571
|
-
def test_process_std_stream_array(null_config):
|
588
|
+
def test_process_std_stream_array(null_config) -> None:
|
572
589
|
cmd = hf.Command(command="", stdout="<<array(parameter:p2)>>")
|
573
590
|
assert np.allclose(
|
574
591
|
cmd.process_std_stream(name="p2", value="1 2 3", stderr=False),
|
@@ -576,7 +593,7 @@ def test_process_std_stream_array(null_config):
|
|
576
593
|
)
|
577
594
|
|
578
595
|
|
579
|
-
def test_process_std_stream_array_delim(null_config):
|
596
|
+
def test_process_std_stream_array_delim(null_config) -> None:
|
580
597
|
cmd = hf.Command(command="", stdout='<<array[delim=","](parameter:p2)>>')
|
581
598
|
assert np.allclose(
|
582
599
|
cmd.process_std_stream(name="p2", value="1,2,3", stderr=False),
|
@@ -584,19 +601,19 @@ def test_process_std_stream_array_delim(null_config):
|
|
584
601
|
)
|
585
602
|
|
586
603
|
|
587
|
-
def test_process_std_stream_array_dtype_int(null_config):
|
604
|
+
def test_process_std_stream_array_dtype_int(null_config) -> None:
|
588
605
|
cmd = hf.Command(command="", stdout="<<array[item_type=int](parameter:p2)>>")
|
589
606
|
arr = cmd.process_std_stream(name="p2", value="1 2 3", stderr=False)
|
590
607
|
assert arr.dtype == np.dtype("int")
|
591
608
|
|
592
609
|
|
593
|
-
def test_process_std_stream_array_dtype_float(null_config):
|
610
|
+
def test_process_std_stream_array_dtype_float(null_config) -> None:
|
594
611
|
cmd = hf.Command(command="", stdout="<<array[item_type=float](parameter:p2)>>")
|
595
612
|
arr = cmd.process_std_stream(name="p2", value="1 2 3", stderr=False)
|
596
613
|
assert arr.dtype == np.dtype("float")
|
597
614
|
|
598
615
|
|
599
|
-
def test_process_std_stream_object(null_config):
|
616
|
+
def test_process_std_stream_object(null_config) -> None:
|
600
617
|
cmd = hf.Command(command="", stdout="<<parameter:p1c>>")
|
601
618
|
a_val = 12
|
602
619
|
assert cmd.process_std_stream(name="p1c", value=str(a_val), stderr=False) == P1(
|
@@ -604,7 +621,7 @@ def test_process_std_stream_object(null_config):
|
|
604
621
|
)
|
605
622
|
|
606
623
|
|
607
|
-
def test_process_std_stream_object_kwargs(null_config):
|
624
|
+
def test_process_std_stream_object_kwargs(null_config) -> None:
|
608
625
|
cmd = hf.Command(command="", stdout="<<parameter:p1c.CLI_parse(double=true)>>")
|
609
626
|
a_val = 12
|
610
627
|
expected = 2 * a_val
|
@@ -613,48 +630,48 @@ def test_process_std_stream_object_kwargs(null_config):
|
|
613
630
|
)
|
614
631
|
|
615
632
|
|
616
|
-
def test_get_output_types(null_config):
|
633
|
+
def test_get_output_types(null_config) -> None:
|
617
634
|
cmd = hf.Command(command="", stdout="<<parameter:p1_test_123>>")
|
618
635
|
assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
|
619
636
|
|
620
637
|
|
621
|
-
def test_get_output_types_int(null_config):
|
638
|
+
def test_get_output_types_int(null_config) -> None:
|
622
639
|
cmd = hf.Command(command="", stdout="<<int(parameter:p1_test_123)>>")
|
623
640
|
assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
|
624
641
|
|
625
642
|
|
626
|
-
def test_get_output_types_object_with_args(null_config):
|
643
|
+
def test_get_output_types_object_with_args(null_config) -> None:
|
627
644
|
cmd = hf.Command(
|
628
645
|
command="", stdout="<<parameter:p1_test_123.CLI_parse(double=true)>>"
|
629
646
|
)
|
630
647
|
assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
|
631
648
|
|
632
649
|
|
633
|
-
def test_get_output_types_list(null_config):
|
650
|
+
def test_get_output_types_list(null_config) -> None:
|
634
651
|
cmd = hf.Command(
|
635
652
|
command="", stdout="<<list[item_type=int, delim=" "](parameter:p1_test_123)>>"
|
636
653
|
)
|
637
654
|
assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
|
638
655
|
|
639
656
|
|
640
|
-
def test_get_output_types_no_match(null_config):
|
657
|
+
def test_get_output_types_no_match(null_config) -> None:
|
641
658
|
cmd = hf.Command(command="", stdout="parameter:p1_test_123")
|
642
659
|
assert cmd.get_output_types() == {"stdout": None, "stderr": None}
|
643
660
|
|
644
661
|
|
645
|
-
def test_get_output_types_raise_with_extra_substring_start(null_config):
|
662
|
+
def test_get_output_types_raise_with_extra_substring_start(null_config) -> None:
|
646
663
|
cmd = hf.Command(command="", stdout="hello: <<parameter:p1_test_123>>")
|
647
664
|
with pytest.raises(ValueError):
|
648
665
|
cmd.get_output_types()
|
649
666
|
|
650
667
|
|
651
|
-
def test_get_output_types_raise_with_extra_substring_end(null_config):
|
668
|
+
def test_get_output_types_raise_with_extra_substring_end(null_config) -> None:
|
652
669
|
cmd = hf.Command(command="", stdout="<<parameter:p1_test_123>> hello")
|
653
670
|
with pytest.raises(ValueError):
|
654
671
|
cmd.get_output_types()
|
655
672
|
|
656
673
|
|
657
|
-
def test_extract_executable_labels(null_config):
|
674
|
+
def test_extract_executable_labels(null_config) -> None:
|
658
675
|
tests = {
|
659
676
|
"<<executable:m1>> and <<executable:12>>": ["m1", "12"],
|
660
677
|
"<<executable:m1>> hi": ["m1"],
|
@@ -1,59 +1,70 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
import os
|
3
|
+
import time
|
2
4
|
import pytest
|
3
5
|
|
4
6
|
from hpcflow.app import app as hf
|
5
|
-
from hpcflow.sdk.config.errors import
|
7
|
+
from hpcflow.sdk.config.errors import (
|
8
|
+
ConfigFileValidationError,
|
9
|
+
ConfigItemCallbackError,
|
10
|
+
ConfigNonConfigurableError,
|
11
|
+
ConfigReadOnlyError,
|
12
|
+
)
|
6
13
|
|
7
14
|
|
8
|
-
def test_reset_config(new_null_config):
|
9
|
-
cfg_dir = hf.config.
|
10
|
-
machine_name = hf.config.
|
15
|
+
def test_reset_config(new_null_config) -> None:
|
16
|
+
cfg_dir = hf.config.config_directory
|
17
|
+
machine_name = hf.config.machine
|
11
18
|
new_machine_name = machine_name + "123"
|
12
|
-
hf.config.
|
13
|
-
assert hf.config.
|
19
|
+
hf.config.machine = new_machine_name
|
20
|
+
assert hf.config.machine == new_machine_name
|
14
21
|
hf.reset_config(config_dir=cfg_dir)
|
15
|
-
assert hf.config.
|
22
|
+
assert hf.config.machine == machine_name
|
16
23
|
|
17
24
|
|
18
|
-
def test_raise_on_invalid_config_file(new_null_config):
|
25
|
+
def test_raise_on_invalid_config_file(new_null_config) -> None:
|
19
26
|
# make an invalid config file:
|
20
|
-
cfg_path = hf.config.
|
27
|
+
cfg_path = hf.config.config_file_path
|
21
28
|
with cfg_path.open("at+") as f:
|
22
29
|
f.write("something_invalid: 1\n")
|
23
30
|
|
24
31
|
# try to load the invalid file:
|
25
|
-
cfg_dir = hf.config.
|
32
|
+
cfg_dir = hf.config.config_directory
|
26
33
|
with pytest.raises(ConfigFileValidationError):
|
27
34
|
hf.reload_config(config_dir=cfg_dir, warn=False)
|
28
35
|
hf.reset_config(config_dir=cfg_dir, warn=False)
|
29
36
|
hf.unload_config()
|
30
37
|
|
31
38
|
|
32
|
-
def test_reset_invalid_config(new_null_config):
|
39
|
+
def test_reset_invalid_config(new_null_config) -> None:
|
33
40
|
# make an invalid config file:
|
34
|
-
cfg_path = hf.config.
|
41
|
+
cfg_path = hf.config.config_file_path
|
35
42
|
with cfg_path.open("at+") as f:
|
36
43
|
f.write("something_invalid: 1\n")
|
37
44
|
|
38
45
|
# check we can reset the invalid file:
|
39
|
-
cfg_dir = hf.config.
|
46
|
+
cfg_dir = hf.config.config_directory
|
40
47
|
hf.reset_config(config_dir=cfg_dir, warn=False)
|
41
48
|
hf.unload_config()
|
42
49
|
|
43
50
|
|
44
|
-
def test_raise_on_set_default_scheduler_not_in_schedulers_list_invalid_name(
|
51
|
+
def test_raise_on_set_default_scheduler_not_in_schedulers_list_invalid_name(
|
52
|
+
null_config,
|
53
|
+
) -> None:
|
45
54
|
new_default = "invalid-scheduler"
|
46
55
|
with pytest.raises(ConfigItemCallbackError):
|
47
56
|
hf.config.default_scheduler = new_default
|
48
57
|
|
49
58
|
|
50
|
-
def test_raise_on_set_default_scheduler_not_in_schedulers_list_valid_name(
|
59
|
+
def test_raise_on_set_default_scheduler_not_in_schedulers_list_valid_name(
|
60
|
+
null_config,
|
61
|
+
) -> None:
|
51
62
|
new_default = "slurm" # valid but unsupported (by default) scheduler
|
52
63
|
with pytest.raises(ConfigItemCallbackError):
|
53
64
|
hf.config.default_scheduler = new_default
|
54
65
|
|
55
66
|
|
56
|
-
def test_without_callbacks_ctx_manager(null_config):
|
67
|
+
def test_without_callbacks_ctx_manager(null_config) -> None:
|
57
68
|
# set a new shell that would raise an error in the `callback_supported_shells`:
|
58
69
|
new_default = "bash" if os.name == "nt" else "powershell"
|
59
70
|
|
@@ -67,3 +78,118 @@ def test_without_callbacks_ctx_manager(null_config):
|
|
67
78
|
|
68
79
|
# unload the modified config so it's not reused by other tests
|
69
80
|
hf.unload_config()
|
81
|
+
|
82
|
+
|
83
|
+
@pytest.mark.xfail(reason="Might occasionally fail.")
|
84
|
+
def test_cache_faster_than_no_cache(null_config):
|
85
|
+
n = 10_000
|
86
|
+
tic = time.perf_counter()
|
87
|
+
for _ in range(n):
|
88
|
+
_ = hf.config.machine
|
89
|
+
toc = time.perf_counter()
|
90
|
+
elapsed_no_cache = toc - tic
|
91
|
+
|
92
|
+
with hf.config.cached_config():
|
93
|
+
tic = time.perf_counter()
|
94
|
+
for _ in range(n):
|
95
|
+
_ = hf.config.machine
|
96
|
+
toc = time.perf_counter()
|
97
|
+
elapsed_cache = toc - tic
|
98
|
+
|
99
|
+
assert elapsed_cache < elapsed_no_cache
|
100
|
+
|
101
|
+
|
102
|
+
def test_cache_read_only(new_null_config):
|
103
|
+
"""Check we cannot modify the config when using the cache"""
|
104
|
+
|
105
|
+
# check we can set an item first:
|
106
|
+
hf.machine = "abc"
|
107
|
+
assert hf.machine == "abc"
|
108
|
+
|
109
|
+
with pytest.raises(ConfigReadOnlyError):
|
110
|
+
with hf.config.cached_config():
|
111
|
+
hf.config.set("machine", "123")
|
112
|
+
|
113
|
+
with pytest.raises(ConfigReadOnlyError):
|
114
|
+
with hf.config.cached_config():
|
115
|
+
hf.config.machine = "456"
|
116
|
+
|
117
|
+
|
118
|
+
def test_workflow_template_config_validation(new_null_config, tmp_path):
|
119
|
+
wkt = hf.WorkflowTemplate(
|
120
|
+
tasks=[],
|
121
|
+
config={"log_file_level": "debug"},
|
122
|
+
name="test_workflow_config_validation",
|
123
|
+
)
|
124
|
+
assert wkt.config == {"log_file_level": "debug"}
|
125
|
+
|
126
|
+
|
127
|
+
def test_workflow_template_config_validation_raises(unload_config, tmp_path):
|
128
|
+
with pytest.raises(ConfigNonConfigurableError):
|
129
|
+
hf.WorkflowTemplate(
|
130
|
+
tasks=[],
|
131
|
+
config={"bad_key": "debug"},
|
132
|
+
name="test_workflow_config_validation_raises",
|
133
|
+
)
|
134
|
+
|
135
|
+
# workflow template config validation should not need to load the whole config:
|
136
|
+
assert not hf.is_config_loaded
|
137
|
+
|
138
|
+
|
139
|
+
def test_config_with_updates(new_null_config):
|
140
|
+
level_1 = hf.config.get("log_console_level")
|
141
|
+
with hf.config._with_updates({"log_console_level": "debug"}):
|
142
|
+
level_2 = hf.config.get("log_console_level")
|
143
|
+
level_3 = hf.config.get("log_console_level")
|
144
|
+
assert level_1 == level_3 != level_2
|
145
|
+
hf.reload_config()
|
146
|
+
|
147
|
+
|
148
|
+
@pytest.mark.integration
|
149
|
+
def test_workflow_template_config_set(new_null_config, tmp_path):
|
150
|
+
"""Test we can set a workflow-level config item and that it is correctly applied
|
151
|
+
during execution."""
|
152
|
+
|
153
|
+
t1 = hf.Task(
|
154
|
+
schema=hf.task_schemas.test_t1_conditional_OS,
|
155
|
+
inputs={"p1": 101},
|
156
|
+
)
|
157
|
+
log_path = tmp_path / "log.log"
|
158
|
+
hf.config.set("log_file_level", "warning")
|
159
|
+
hf.config.set("log_file_path", log_path)
|
160
|
+
|
161
|
+
log_str_1 = "this should not appear in the log file"
|
162
|
+
hf.submission_logger.debug(log_str_1)
|
163
|
+
|
164
|
+
log_str_2 = "this should appear in the log file"
|
165
|
+
hf.submission_logger.warning(log_str_2)
|
166
|
+
|
167
|
+
assert log_path.is_file()
|
168
|
+
log_file_contents = log_path.read_text()
|
169
|
+
assert log_str_1 not in log_file_contents
|
170
|
+
assert log_str_2 in log_file_contents
|
171
|
+
|
172
|
+
wk = hf.Workflow.from_template_data(
|
173
|
+
tasks=[t1],
|
174
|
+
config={"log_file_level": "debug"},
|
175
|
+
resources={"any": {"write_app_logs": True}},
|
176
|
+
workflow_name="test_workflow_config",
|
177
|
+
template_name="test_workflow_config",
|
178
|
+
path=tmp_path,
|
179
|
+
)
|
180
|
+
wk.submit(wait=True, status=False, add_to_known=False)
|
181
|
+
|
182
|
+
# check some DEBUG messages present in the run logs
|
183
|
+
debug_str = " DEBUG hpcflow.persistence:"
|
184
|
+
|
185
|
+
run = wk.get_EARs_from_IDs([0])[0]
|
186
|
+
run_log_path = run.get_app_log_path()
|
187
|
+
assert run_log_path.is_file()
|
188
|
+
|
189
|
+
run_log_contents = run_log_path.read_text()
|
190
|
+
assert debug_str in run_log_contents
|
191
|
+
|
192
|
+
# log file level should not have changed:
|
193
|
+
assert hf.config.get("log_file_level") == "warning"
|
194
|
+
|
195
|
+
hf.reload_config()
|