hpcflow 0.1.9__py3-none-any.whl → 0.2.0a271__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/__init__.py +2 -11
- hpcflow/__pyinstaller/__init__.py +5 -0
- hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
- hpcflow/_version.py +1 -1
- hpcflow/app.py +43 -0
- hpcflow/cli.py +2 -462
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
- hpcflow/data/jinja_templates/test/test_template.txt +8 -0
- hpcflow/data/programs/hello_world/README.md +1 -0
- hpcflow/data/programs/hello_world/hello_world.c +87 -0
- hpcflow/data/programs/hello_world/linux/hello_world +0 -0
- hpcflow/data/programs/hello_world/macos/hello_world +0 -0
- hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
- hpcflow/data/scripts/__init__.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
- hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -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/generate_t1_file_01.py +7 -0
- hpcflow/data/scripts/import_future_script.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.py +6 -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_direct_out_all_iters_test.py +15 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -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_direct_sub_param_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
- hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
- hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -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/parse_t1_file_01.py +4 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/__init__.py +1 -0
- hpcflow/data/template_components/command_files.yaml +26 -0
- hpcflow/data/template_components/environments.yaml +13 -0
- hpcflow/data/template_components/parameters.yaml +14 -0
- hpcflow/data/template_components/task_schemas.yaml +139 -0
- hpcflow/data/workflows/workflow_1.yaml +5 -0
- hpcflow/examples.ipynb +1037 -0
- hpcflow/sdk/__init__.py +149 -0
- hpcflow/sdk/app.py +4266 -0
- hpcflow/sdk/cli.py +1479 -0
- hpcflow/sdk/cli_common.py +385 -0
- hpcflow/sdk/config/__init__.py +5 -0
- hpcflow/sdk/config/callbacks.py +246 -0
- hpcflow/sdk/config/cli.py +388 -0
- hpcflow/sdk/config/config.py +1410 -0
- hpcflow/sdk/config/config_file.py +501 -0
- hpcflow/sdk/config/errors.py +272 -0
- hpcflow/sdk/config/types.py +150 -0
- hpcflow/sdk/core/__init__.py +38 -0
- hpcflow/sdk/core/actions.py +3857 -0
- hpcflow/sdk/core/app_aware.py +25 -0
- hpcflow/sdk/core/cache.py +224 -0
- hpcflow/sdk/core/command_files.py +814 -0
- hpcflow/sdk/core/commands.py +424 -0
- hpcflow/sdk/core/element.py +2071 -0
- hpcflow/sdk/core/enums.py +221 -0
- hpcflow/sdk/core/environment.py +256 -0
- hpcflow/sdk/core/errors.py +1043 -0
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +809 -0
- hpcflow/sdk/core/loop.py +1320 -0
- hpcflow/sdk/core/loop_cache.py +282 -0
- hpcflow/sdk/core/object_list.py +933 -0
- hpcflow/sdk/core/parameters.py +3371 -0
- hpcflow/sdk/core/rule.py +196 -0
- hpcflow/sdk/core/run_dir_files.py +57 -0
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +3792 -0
- hpcflow/sdk/core/task_schema.py +993 -0
- hpcflow/sdk/core/test_utils.py +538 -0
- hpcflow/sdk/core/types.py +447 -0
- hpcflow/sdk/core/utils.py +1207 -0
- hpcflow/sdk/core/validation.py +87 -0
- hpcflow/sdk/core/values.py +477 -0
- hpcflow/sdk/core/workflow.py +4820 -0
- hpcflow/sdk/core/zarr_io.py +206 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/data/config_file_schema.yaml +34 -0
- hpcflow/sdk/data/config_schema.yaml +260 -0
- hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
- hpcflow/sdk/data/files_spec_schema.yaml +5 -0
- hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
- hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
- hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/demo/cli.py +242 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +137 -0
- hpcflow/sdk/helper/helper.py +300 -0
- hpcflow/sdk/helper/watcher.py +192 -0
- hpcflow/sdk/log.py +288 -0
- hpcflow/sdk/persistence/__init__.py +18 -0
- hpcflow/sdk/persistence/base.py +2817 -0
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +39 -0
- hpcflow/sdk/persistence/json.py +954 -0
- hpcflow/sdk/persistence/pending.py +948 -0
- hpcflow/sdk/persistence/store_resource.py +203 -0
- hpcflow/sdk/persistence/types.py +309 -0
- hpcflow/sdk/persistence/utils.py +73 -0
- hpcflow/sdk/persistence/zarr.py +2388 -0
- hpcflow/sdk/runtime.py +320 -0
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/enums.py +70 -0
- hpcflow/sdk/submission/jobscript.py +2379 -0
- hpcflow/sdk/submission/schedulers/__init__.py +281 -0
- hpcflow/sdk/submission/schedulers/direct.py +233 -0
- hpcflow/sdk/submission/schedulers/sge.py +376 -0
- hpcflow/sdk/submission/schedulers/slurm.py +598 -0
- hpcflow/sdk/submission/schedulers/utils.py +25 -0
- hpcflow/sdk/submission/shells/__init__.py +52 -0
- hpcflow/sdk/submission/shells/base.py +229 -0
- hpcflow/sdk/submission/shells/bash.py +504 -0
- hpcflow/sdk/submission/shells/os_version.py +115 -0
- hpcflow/sdk/submission/shells/powershell.py +352 -0
- hpcflow/sdk/submission/submission.py +1402 -0
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +194 -0
- hpcflow/sdk/utils/arrays.py +69 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +31 -0
- hpcflow/sdk/utils/strings.py +69 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +123 -0
- hpcflow/tests/data/__init__.py +0 -0
- hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
- hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_1.json +10 -0
- hpcflow/tests/data/workflow_1.yaml +5 -0
- hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
- hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
- hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
- hpcflow/tests/programs/test_programs.py +180 -0
- hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +1361 -0
- 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 +14 -0
- hpcflow/tests/unit/test_action.py +1066 -0
- hpcflow/tests/unit/test_action_rule.py +24 -0
- hpcflow/tests/unit/test_app.py +132 -0
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +172 -0
- hpcflow/tests/unit/test_command.py +377 -0
- hpcflow/tests/unit/test_config.py +195 -0
- hpcflow/tests/unit/test_config_file.py +162 -0
- hpcflow/tests/unit/test_element.py +666 -0
- hpcflow/tests/unit/test_element_iteration.py +88 -0
- hpcflow/tests/unit/test_element_set.py +158 -0
- hpcflow/tests/unit/test_group.py +115 -0
- hpcflow/tests/unit/test_input_source.py +1479 -0
- hpcflow/tests/unit/test_input_value.py +398 -0
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +1247 -0
- hpcflow/tests/unit/test_loop.py +2674 -0
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
- hpcflow/tests/unit/test_object_list.py +116 -0
- hpcflow/tests/unit/test_parameter.py +243 -0
- hpcflow/tests/unit/test_persistence.py +664 -0
- hpcflow/tests/unit/test_resources.py +243 -0
- hpcflow/tests/unit/test_run.py +286 -0
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +9 -0
- hpcflow/tests/unit/test_schema_input.py +372 -0
- hpcflow/tests/unit/test_shell.py +129 -0
- hpcflow/tests/unit/test_slurm.py +39 -0
- hpcflow/tests/unit/test_submission.py +502 -0
- hpcflow/tests/unit/test_task.py +2560 -0
- hpcflow/tests/unit/test_task_schema.py +182 -0
- hpcflow/tests/unit/test_utils.py +616 -0
- hpcflow/tests/unit/test_value_sequence.py +549 -0
- hpcflow/tests/unit/test_values.py +91 -0
- hpcflow/tests/unit/test_workflow.py +827 -0
- hpcflow/tests/unit/test_workflow_template.py +186 -0
- 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/unit/utils/test_strings.py +97 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +355 -0
- 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 +564 -0
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6794 -0
- hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
- hpcflow-0.2.0a271.dist-info/METADATA +65 -0
- hpcflow-0.2.0a271.dist-info/RECORD +237 -0
- {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
- hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
- hpcflow/api.py +0 -458
- hpcflow/archive/archive.py +0 -308
- hpcflow/archive/cloud/cloud.py +0 -47
- hpcflow/archive/cloud/errors.py +0 -9
- hpcflow/archive/cloud/providers/dropbox.py +0 -432
- hpcflow/archive/errors.py +0 -5
- hpcflow/base_db.py +0 -4
- hpcflow/config.py +0 -232
- hpcflow/copytree.py +0 -66
- hpcflow/data/examples/_config.yml +0 -14
- hpcflow/data/examples/damask/demo/1.run.yml +0 -4
- hpcflow/data/examples/damask/demo/2.process.yml +0 -29
- hpcflow/data/examples/damask/demo/geom.geom +0 -2052
- hpcflow/data/examples/damask/demo/load.load +0 -1
- hpcflow/data/examples/damask/demo/material.config +0 -185
- hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
- hpcflow/data/examples/damask/inputs/load.load +0 -1
- hpcflow/data/examples/damask/inputs/material.config +0 -185
- hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
- hpcflow/data/examples/damask/profiles/damask.yml +0 -4
- hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
- hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
- hpcflow/data/examples/damask/profiles/default.yml +0 -6
- hpcflow/data/examples/thinking.yml +0 -177
- hpcflow/errors.py +0 -2
- hpcflow/init_db.py +0 -37
- hpcflow/models.py +0 -2549
- hpcflow/nesting.py +0 -9
- hpcflow/profiles.py +0 -455
- hpcflow/project.py +0 -81
- hpcflow/scheduler.py +0 -323
- hpcflow/utils.py +0 -103
- hpcflow/validation.py +0 -167
- hpcflow/variables.py +0 -544
- hpcflow-0.1.9.dist-info/METADATA +0 -168
- hpcflow-0.1.9.dist-info/RECORD +0 -45
- hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
- hpcflow-0.1.9.dist-info/top_level.txt +0 -1
- /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
- /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
- /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
import pytest
|
|
3
|
+
from hpcflow.app import app as hf
|
|
4
|
+
from hpcflow.sdk.core.errors import MissingVariableSubstitutionError
|
|
5
|
+
from hpcflow.sdk.core.test_utils import (
|
|
6
|
+
make_test_data_YAML_workflow_template,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_merge_template_level_resources_into_element_set(null_config):
|
|
11
|
+
wkt = hf.WorkflowTemplate(
|
|
12
|
+
name="w1",
|
|
13
|
+
tasks=[hf.Task(schema=[hf.task_schemas.test_t1_ps])],
|
|
14
|
+
resources={"any": {"num_cores": 1}},
|
|
15
|
+
)
|
|
16
|
+
assert wkt.tasks[0].element_sets[0].resources == hf.ResourceList.from_json_like(
|
|
17
|
+
{"any": {"num_cores": 1}}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_equivalence_from_YAML_and_JSON_files(null_config):
|
|
22
|
+
wkt_yaml = make_test_data_YAML_workflow_template("workflow_1.yaml")
|
|
23
|
+
wkt_json = make_test_data_YAML_workflow_template("workflow_1.json")
|
|
24
|
+
assert wkt_json == wkt_yaml
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_reuse(null_config, tmp_path):
|
|
28
|
+
"""Test we can re-use a template that has already been made persistent."""
|
|
29
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[])
|
|
30
|
+
wk1 = hf.Workflow.from_template(wkt, name="test_1", path=tmp_path)
|
|
31
|
+
wk2 = hf.Workflow.from_template(wkt, name="test_2", path=tmp_path)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_workflow_template_vars(tmp_path, new_null_config):
|
|
35
|
+
num_repeats = 2
|
|
36
|
+
wkt = make_test_data_YAML_workflow_template(
|
|
37
|
+
workflow_name="benchmark_N_elements.yaml",
|
|
38
|
+
variables={"N": num_repeats},
|
|
39
|
+
)
|
|
40
|
+
assert wkt.tasks[0].element_sets[0].repeats[0]["number"] == num_repeats
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_workflow_template_vars_raise_no_vars(tmp_path, new_null_config):
|
|
44
|
+
# no default value for the variable is provided in `benchmark_N_elements`, so should
|
|
45
|
+
# raise if the variables dict is not passed:
|
|
46
|
+
with pytest.raises(MissingVariableSubstitutionError):
|
|
47
|
+
make_test_data_YAML_workflow_template("benchmark_N_elements.yaml")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_workflow_template_vars_defaults_used(tmp_path, new_null_config):
|
|
51
|
+
# `benchmark_script_runner` contains a default value for the variable `N`, so that
|
|
52
|
+
# should be used, since we don't pass any variables:
|
|
53
|
+
wkt = make_test_data_YAML_workflow_template("benchmark_script_runner.yaml")
|
|
54
|
+
assert wkt.tasks[0].element_sets[0].repeats[0]["number"] == 1
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_workflow_template_vars_False_no_substitution(tmp_path, new_null_config):
|
|
58
|
+
# read a yaml template, check variables are not substituted, when `variables=False`:
|
|
59
|
+
wkt_yaml = dedent(
|
|
60
|
+
"""\
|
|
61
|
+
name: workflow_1
|
|
62
|
+
tasks:
|
|
63
|
+
- schema: test_t1_conditional_OS
|
|
64
|
+
inputs:
|
|
65
|
+
p1: <<var:my_var>>
|
|
66
|
+
"""
|
|
67
|
+
)
|
|
68
|
+
wkt = hf.WorkflowTemplate.from_YAML_string(wkt_yaml, variables=False)
|
|
69
|
+
assert wkt.tasks[0].element_sets[0].inputs[0].value == "<<var:my_var>>"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_env_preset_merge_simple(null_config):
|
|
73
|
+
s1 = hf.TaskSchema(
|
|
74
|
+
objective="s1",
|
|
75
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
76
|
+
environment_presets={"my_env_preset": {"my_env": {"version": 1}}},
|
|
77
|
+
)
|
|
78
|
+
wkt = hf.WorkflowTemplate(
|
|
79
|
+
name="test",
|
|
80
|
+
env_presets="my_env_preset",
|
|
81
|
+
tasks=[hf.Task(schema=s1)],
|
|
82
|
+
)
|
|
83
|
+
assert wkt.tasks[0].element_sets[0].env_preset == "my_env_preset"
|
|
84
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
85
|
+
"my_env": {"version": 1}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_env_preset_merge_simple_list(null_config):
|
|
90
|
+
s1 = hf.TaskSchema(
|
|
91
|
+
objective="s1",
|
|
92
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
93
|
+
environment_presets={"my_env_preset": {"my_env": {"version": 1}}},
|
|
94
|
+
)
|
|
95
|
+
wkt = hf.WorkflowTemplate(
|
|
96
|
+
name="test",
|
|
97
|
+
env_presets=["my_env_preset", "my_other_env_preset"],
|
|
98
|
+
tasks=[hf.Task(schema=s1)],
|
|
99
|
+
)
|
|
100
|
+
assert wkt.tasks[0].element_sets[0].env_preset == "my_env_preset"
|
|
101
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
102
|
+
"my_env": {"version": 1}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_env_preset_no_merge_existing_env_preset(null_config):
|
|
107
|
+
s1 = hf.TaskSchema(
|
|
108
|
+
objective="s1",
|
|
109
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
110
|
+
environment_presets={
|
|
111
|
+
"env_preset_1": {"my_env": {"version": 1}},
|
|
112
|
+
"env_preset_2": {"my_env": {"version": 2}},
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
wkt = hf.WorkflowTemplate(
|
|
116
|
+
name="test",
|
|
117
|
+
env_presets="env_preset_1",
|
|
118
|
+
tasks=[hf.Task(schema=s1, env_preset="env_preset_2")],
|
|
119
|
+
)
|
|
120
|
+
assert wkt.tasks[0].element_sets[0].env_preset == "env_preset_2"
|
|
121
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
122
|
+
"my_env": {"version": 2}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_environments_merge_simple(null_config):
|
|
127
|
+
s1 = hf.TaskSchema(
|
|
128
|
+
objective="s1",
|
|
129
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
130
|
+
)
|
|
131
|
+
wkt = hf.WorkflowTemplate(
|
|
132
|
+
name="test",
|
|
133
|
+
environments={"my_env": {"version": 1}, "my_other_env": {"version": 2}},
|
|
134
|
+
tasks=[hf.Task(schema=s1)],
|
|
135
|
+
)
|
|
136
|
+
assert wkt.tasks[0].element_sets[0].environments == {"my_env": {"version": 1}}
|
|
137
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
138
|
+
"my_env": {"version": 1}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_environments_no_merge_existing_envs(null_config):
|
|
143
|
+
s1 = hf.TaskSchema(
|
|
144
|
+
objective="s1",
|
|
145
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
146
|
+
)
|
|
147
|
+
wkt = hf.WorkflowTemplate(
|
|
148
|
+
name="test",
|
|
149
|
+
environments={"my_env": {"version": 1}, "my_other_env": {"version": 2}},
|
|
150
|
+
tasks=[hf.Task(schema=s1, environments={"my_env": {"version": 2}})],
|
|
151
|
+
)
|
|
152
|
+
assert wkt.tasks[0].element_sets[0].environments == {"my_env": {"version": 2}}
|
|
153
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
154
|
+
"my_env": {"version": 2}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_raise_on_env_preset_and_environments(null_config):
|
|
159
|
+
with pytest.raises(ValueError):
|
|
160
|
+
wkt = hf.WorkflowTemplate(
|
|
161
|
+
name="test",
|
|
162
|
+
env_presets="my_env_preset",
|
|
163
|
+
environments={"my_env": {"version": 1}},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_default_env_preset_used_if_available(null_config):
|
|
168
|
+
"""Test that if no env_presets or environments are specified at template-level or task
|
|
169
|
+
level, the default (named as an empty string) env preset is used if available."""
|
|
170
|
+
|
|
171
|
+
s1 = hf.TaskSchema(
|
|
172
|
+
objective="s1",
|
|
173
|
+
actions=[hf.Action(environments=[hf.ActionEnvironment("my_env")])],
|
|
174
|
+
environment_presets={
|
|
175
|
+
"": {"my_env": {"version": 1}},
|
|
176
|
+
"env_preset_1": {"my_env": {"version": 2}},
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
wkt = hf.WorkflowTemplate(
|
|
180
|
+
name="test",
|
|
181
|
+
tasks=[hf.Task(schema=s1)],
|
|
182
|
+
)
|
|
183
|
+
assert wkt.tasks[0].element_sets[0].env_preset == ""
|
|
184
|
+
assert wkt.tasks[0].element_sets[0].resources[0].environments == {
|
|
185
|
+
"my_env": {"version": 1}
|
|
186
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from hpcflow.sdk.utils.arrays import get_1D_idx, get_2D_idx, split_arr
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_get_2D_idx():
|
|
7
|
+
assert get_2D_idx(0, num_cols=10) == (0, 0)
|
|
8
|
+
assert get_2D_idx(4, num_cols=10) == (0, 4)
|
|
9
|
+
assert get_2D_idx(9, num_cols=10) == (0, 9)
|
|
10
|
+
assert get_2D_idx(10, num_cols=10) == (1, 0)
|
|
11
|
+
assert get_2D_idx(13, num_cols=10) == (1, 3)
|
|
12
|
+
assert get_2D_idx(20, num_cols=10) == (2, 0)
|
|
13
|
+
arr_r, arr_c = get_2D_idx(np.array([0, 4, 9, 10, 13, 20]), num_cols=10)
|
|
14
|
+
assert np.array_equal(arr_r, np.array([0, 0, 0, 1, 1, 2]))
|
|
15
|
+
assert np.array_equal(arr_c, np.array([0, 4, 9, 0, 3, 0]))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_get_1D_idx():
|
|
19
|
+
assert get_1D_idx(*(0, 0), num_cols=10) == 0
|
|
20
|
+
assert get_1D_idx(*(0, 4), num_cols=10) == 4
|
|
21
|
+
assert get_1D_idx(*(0, 9), num_cols=10) == 9
|
|
22
|
+
assert get_1D_idx(*(1, 0), num_cols=10) == 10
|
|
23
|
+
assert get_1D_idx(*(1, 3), num_cols=10) == 13
|
|
24
|
+
assert get_1D_idx(*(2, 0), num_cols=10) == 20
|
|
25
|
+
|
|
26
|
+
assert np.array_equal(
|
|
27
|
+
get_1D_idx(
|
|
28
|
+
np.array([0, 0, 0, 1, 1, 2]), np.array([0, 4, 9, 0, 3, 0]), num_cols=10
|
|
29
|
+
),
|
|
30
|
+
np.array([0, 4, 9, 10, 13, 20]),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_split_arr():
|
|
35
|
+
splt = split_arr(np.array([4, 0, 1, 2, 3, 4, 1, 4, 5, 6]), metadata_size=1)
|
|
36
|
+
assert len(splt) == 2
|
|
37
|
+
assert np.array_equal(splt[0][0], np.array([0]))
|
|
38
|
+
assert np.array_equal(splt[0][1], np.array([1, 2, 3]))
|
|
39
|
+
assert np.array_equal(splt[1][0], np.array([1]))
|
|
40
|
+
assert np.array_equal(splt[1][1], np.array([4, 5, 6]))
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from hpcflow.sdk.utils.deferred_file import DeferredFileWriter
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_file_not_created(tmp_path):
|
|
5
|
+
file_name = tmp_path / "test.txt"
|
|
6
|
+
assert not file_name.is_file()
|
|
7
|
+
with DeferredFileWriter(file_name, mode="w") as fp:
|
|
8
|
+
assert not fp._is_open
|
|
9
|
+
assert not file_name.is_file()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_append_file_not_opened(tmp_path):
|
|
13
|
+
file_name = tmp_path / "test.txt"
|
|
14
|
+
with DeferredFileWriter(file_name, mode="a") as fp:
|
|
15
|
+
assert not fp._is_open
|
|
16
|
+
assert not file_name.is_file()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_file_created_write(tmp_path):
|
|
20
|
+
file_name = tmp_path / "test.txt"
|
|
21
|
+
assert not file_name.is_file()
|
|
22
|
+
with DeferredFileWriter(file_name, mode="w") as fp:
|
|
23
|
+
fp.write("contents\n")
|
|
24
|
+
assert fp._is_open
|
|
25
|
+
assert file_name.is_file()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_file_created_writelines(tmp_path):
|
|
29
|
+
file_name = tmp_path / "test.txt"
|
|
30
|
+
assert not file_name.is_file()
|
|
31
|
+
with DeferredFileWriter(file_name, mode="w") as fp:
|
|
32
|
+
fp.writelines(["contents\n"])
|
|
33
|
+
assert fp._is_open
|
|
34
|
+
assert file_name.is_file()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from hpcflow.sdk.utils.hashing import get_hash
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_get_hash_simple_types_is_int():
|
|
5
|
+
assert isinstance(get_hash(1), int)
|
|
6
|
+
assert isinstance(get_hash(3.2), int)
|
|
7
|
+
assert isinstance(get_hash("a"), int)
|
|
8
|
+
assert isinstance(get_hash("abc"), int)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_get_hash_compound_types_is_int():
|
|
12
|
+
assert isinstance(get_hash([1, 2, 3]), int)
|
|
13
|
+
assert isinstance(get_hash((1, 2, 3)), int)
|
|
14
|
+
assert isinstance(get_hash({1, 2, 3}), int)
|
|
15
|
+
assert isinstance(get_hash({"a": 1, "b": 2, "c": 3}), int)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_get_hash_nested_dict_is_int():
|
|
19
|
+
assert isinstance(get_hash({"a": {"b": {"c": [1, 2, 3, ("4", 5, 6)]}}}), int)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_get_hash_distinct_simple_types():
|
|
23
|
+
assert get_hash(1) != get_hash(2)
|
|
24
|
+
assert get_hash(2.2) != get_hash(2.3)
|
|
25
|
+
assert get_hash("a") != get_hash("b")
|
|
26
|
+
assert get_hash("abc") != get_hash("ABC")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_get_hash_distinct_compound_types():
|
|
30
|
+
assert get_hash([1, 2, 3]) != get_hash([1, 2, 4])
|
|
31
|
+
assert get_hash((1, 2, 3)) != get_hash((1, 2, 4))
|
|
32
|
+
assert get_hash({1, 2, 3}) != get_hash({1, 2, 4})
|
|
33
|
+
assert get_hash({"a": 1, "b": 2, "c": 3}) != get_hash({"a": 1, "b": 2, "c": 4})
|
|
34
|
+
assert get_hash({"a": {"b": {"c": [1, 2, 3, ("4", 5, 7)]}}}) == get_hash(
|
|
35
|
+
{"a": {"b": {"c": [1, 2, 3, ("4", 5, 7)]}}}
|
|
36
|
+
)
|
|
37
|
+
assert get_hash({"a": 1}) != get_hash(1) != get_hash("a")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_get_hash_equal_simple_types():
|
|
41
|
+
assert get_hash(1) == get_hash(1)
|
|
42
|
+
assert get_hash(2.2) == get_hash(2.2)
|
|
43
|
+
assert get_hash("a") == get_hash("a")
|
|
44
|
+
assert get_hash("abc") == get_hash("abc")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_get_hash_equal_compound_types():
|
|
48
|
+
assert get_hash([1, 2, 3]) == get_hash([1, 2, 3])
|
|
49
|
+
assert get_hash((1, 2, 3)) == get_hash((1, 2, 3))
|
|
50
|
+
assert get_hash({1, 2, 3}) == get_hash({1, 2, 3})
|
|
51
|
+
assert get_hash({"a": 1, "b": 2, "c": 3}) == get_hash({"a": 1, "b": 2, "c": 3})
|
|
52
|
+
assert get_hash({"a": {"b": {"c": [1, 2, 3, ("4", 5, 6)]}}}) == get_hash(
|
|
53
|
+
{"a": {"b": {"c": [1, 2, 3, ("4", 5, 6)]}}}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_get_hash_order_insensitivity():
|
|
58
|
+
assert get_hash({"a": 1, "b": 2}) == get_hash({"b": 2, "a": 1})
|
|
59
|
+
assert get_hash({1, 2, 3}) == get_hash({2, 3, 1})
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_get_hash_order_sensitivity():
|
|
63
|
+
assert get_hash([1, 2, 3]) != get_hash([2, 3, 1])
|
|
64
|
+
assert get_hash((1, 2, 3)) != get_hash((2, 3, 1))
|
|
65
|
+
assert get_hash("abc") != get_hash("cba")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from hpcflow.sdk.core.utils import redirect_std_to_file
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_stdout_redirect(tmp_path):
|
|
8
|
+
file_name = tmp_path / "test.txt"
|
|
9
|
+
expected = "stdout"
|
|
10
|
+
with redirect_std_to_file(file_name, mode="w"):
|
|
11
|
+
print(expected)
|
|
12
|
+
with file_name.open("r") as fp:
|
|
13
|
+
contents = fp.read().strip()
|
|
14
|
+
assert contents == expected
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_stderr_redirect(tmp_path):
|
|
18
|
+
file_name = tmp_path / "test.txt"
|
|
19
|
+
expected = "stderr"
|
|
20
|
+
with redirect_std_to_file(file_name, mode="w"):
|
|
21
|
+
print(expected, file=sys.stderr)
|
|
22
|
+
with file_name.open("r") as fp:
|
|
23
|
+
contents = fp.read().strip()
|
|
24
|
+
assert contents == expected
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_exception_exits_with_code(tmp_path):
|
|
28
|
+
file_name = tmp_path / "test.txt"
|
|
29
|
+
with pytest.raises(SystemExit) as exc:
|
|
30
|
+
with redirect_std_to_file(file_name, mode="w"):
|
|
31
|
+
raise ValueError("oh no!")
|
|
32
|
+
assert exc.value.code == 1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_exception_prints_to_file(tmp_path):
|
|
36
|
+
file_name = tmp_path / "test.txt"
|
|
37
|
+
with pytest.raises(SystemExit):
|
|
38
|
+
with redirect_std_to_file(file_name, mode="w"):
|
|
39
|
+
raise ValueError("oh no!")
|
|
40
|
+
with file_name.open("r") as fp:
|
|
41
|
+
contents = fp.read().strip()
|
|
42
|
+
assert 'ValueError("oh no!")' in contents
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_file_not_created(tmp_path):
|
|
46
|
+
file_name = tmp_path / "test.txt"
|
|
47
|
+
assert not file_name.is_file()
|
|
48
|
+
with redirect_std_to_file(file_name, mode="w"):
|
|
49
|
+
pass
|
|
50
|
+
assert not file_name.is_file()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
|
|
3
|
+
from hpcflow.sdk.utils.strings import extract_py_from_future_imports
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_extract_py_from_future_imports_none():
|
|
7
|
+
py_str = dedent(
|
|
8
|
+
"""\
|
|
9
|
+
|
|
10
|
+
def my_function():
|
|
11
|
+
print("blah!")
|
|
12
|
+
"""
|
|
13
|
+
)
|
|
14
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
|
15
|
+
assert imports == set()
|
|
16
|
+
assert new_str == py_str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_extract_py_from_future_imports_single():
|
|
20
|
+
py_str = dedent(
|
|
21
|
+
"""\
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
def my_function():
|
|
25
|
+
print("blah!")
|
|
26
|
+
"""
|
|
27
|
+
)
|
|
28
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
|
29
|
+
assert imports == {"annotations"}
|
|
30
|
+
assert new_str == dedent(
|
|
31
|
+
"""\
|
|
32
|
+
|
|
33
|
+
def my_function():
|
|
34
|
+
print("blah!")
|
|
35
|
+
"""
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_extract_py_from_future_imports_multi():
|
|
40
|
+
py_str = dedent(
|
|
41
|
+
"""\
|
|
42
|
+
from __future__ import annotations, feature_2
|
|
43
|
+
|
|
44
|
+
def my_function():
|
|
45
|
+
print("blah!")
|
|
46
|
+
"""
|
|
47
|
+
)
|
|
48
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
|
49
|
+
assert imports == {"annotations", "feature_2"}
|
|
50
|
+
assert new_str == dedent(
|
|
51
|
+
"""\
|
|
52
|
+
|
|
53
|
+
def my_function():
|
|
54
|
+
print("blah!")
|
|
55
|
+
"""
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_extract_py_from_future_imports_trailing_comma():
|
|
60
|
+
py_str = dedent(
|
|
61
|
+
"""\
|
|
62
|
+
from __future__ import annotations,
|
|
63
|
+
|
|
64
|
+
def my_function():
|
|
65
|
+
print("blah!")
|
|
66
|
+
"""
|
|
67
|
+
)
|
|
68
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
|
69
|
+
assert imports == {"annotations"}
|
|
70
|
+
assert new_str == dedent(
|
|
71
|
+
"""\
|
|
72
|
+
|
|
73
|
+
def my_function():
|
|
74
|
+
print("blah!")
|
|
75
|
+
"""
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_extract_py_from_future_imports_multi_lines():
|
|
80
|
+
py_str = dedent(
|
|
81
|
+
"""\
|
|
82
|
+
from __future__ import annotations, feature_2
|
|
83
|
+
from __future__ import feature_2, feature_3,
|
|
84
|
+
|
|
85
|
+
def my_function():
|
|
86
|
+
print("blah!")
|
|
87
|
+
"""
|
|
88
|
+
)
|
|
89
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
|
90
|
+
assert imports == {"annotations", "feature_2", "feature_3"}
|
|
91
|
+
assert new_str == dedent(
|
|
92
|
+
"""\
|
|
93
|
+
|
|
94
|
+
def my_function():
|
|
95
|
+
print("blah!")
|
|
96
|
+
"""
|
|
97
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Tests concerning the directory structure of a created or submitted workflow"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from hpcflow.sdk.core.test_utils import (
|
|
8
|
+
make_test_data_YAML_workflow,
|
|
9
|
+
make_workflow_to_run_command,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.integration
|
|
14
|
+
def test_std_stream_file_not_created(tmp_path, new_null_config):
|
|
15
|
+
"""Normally, the app standard stream file should not be written."""
|
|
16
|
+
wk = make_test_data_YAML_workflow("workflow_1.yaml", path=tmp_path)
|
|
17
|
+
wk.submit(wait=True, add_to_known=False)
|
|
18
|
+
run = wk.get_all_EARs()[0]
|
|
19
|
+
std_stream_path = run.get_app_std_path()
|
|
20
|
+
assert not std_stream_path.is_file()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.integration
|
|
24
|
+
def test_std_stream_file_created_on_exception_raised(tmp_path, new_null_config):
|
|
25
|
+
command = 'wkflow_app --std-stream "$HPCFLOW_RUN_STD_PATH" internal noop --raise'
|
|
26
|
+
wk = make_workflow_to_run_command(command=command, path=tmp_path)
|
|
27
|
+
wk.submit(wait=True, add_to_known=False)
|
|
28
|
+
run = wk.get_all_EARs()[0]
|
|
29
|
+
std_stream_path = run.get_app_std_path()
|
|
30
|
+
assert std_stream_path.is_file()
|
|
31
|
+
assert "ValueError: internal noop raised!" in std_stream_path.read_text()
|