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.
Files changed (275) hide show
  1. hpcflow/__init__.py +2 -11
  2. hpcflow/__pyinstaller/__init__.py +5 -0
  3. hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
  4. hpcflow/_version.py +1 -1
  5. hpcflow/app.py +43 -0
  6. hpcflow/cli.py +2 -462
  7. hpcflow/data/demo_data_manifest/__init__.py +3 -0
  8. hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
  9. hpcflow/data/jinja_templates/test/test_template.txt +8 -0
  10. hpcflow/data/programs/hello_world/README.md +1 -0
  11. hpcflow/data/programs/hello_world/hello_world.c +87 -0
  12. hpcflow/data/programs/hello_world/linux/hello_world +0 -0
  13. hpcflow/data/programs/hello_world/macos/hello_world +0 -0
  14. hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
  15. hpcflow/data/scripts/__init__.py +1 -0
  16. hpcflow/data/scripts/bad_script.py +2 -0
  17. hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
  18. hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
  19. hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
  20. hpcflow/data/scripts/do_nothing.py +2 -0
  21. hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
  22. hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
  23. hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
  24. hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
  25. hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
  26. hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
  27. hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
  28. hpcflow/data/scripts/generate_t1_file_01.py +7 -0
  29. hpcflow/data/scripts/import_future_script.py +7 -0
  30. hpcflow/data/scripts/input_file_generator_basic.py +3 -0
  31. hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
  32. hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
  33. hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
  34. hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
  35. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
  36. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
  37. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
  38. hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
  39. hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
  40. hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
  41. hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
  42. hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
  43. hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
  44. hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
  45. hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
  46. hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
  47. hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
  48. hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
  49. hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
  50. hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
  51. hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
  52. hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
  53. hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
  54. hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
  55. hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
  56. hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
  57. hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
  58. hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
  59. hpcflow/data/scripts/output_file_parser_basic.py +3 -0
  60. hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
  61. hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
  62. hpcflow/data/scripts/parse_t1_file_01.py +4 -0
  63. hpcflow/data/scripts/script_exit_test.py +5 -0
  64. hpcflow/data/template_components/__init__.py +1 -0
  65. hpcflow/data/template_components/command_files.yaml +26 -0
  66. hpcflow/data/template_components/environments.yaml +13 -0
  67. hpcflow/data/template_components/parameters.yaml +14 -0
  68. hpcflow/data/template_components/task_schemas.yaml +139 -0
  69. hpcflow/data/workflows/workflow_1.yaml +5 -0
  70. hpcflow/examples.ipynb +1037 -0
  71. hpcflow/sdk/__init__.py +149 -0
  72. hpcflow/sdk/app.py +4266 -0
  73. hpcflow/sdk/cli.py +1479 -0
  74. hpcflow/sdk/cli_common.py +385 -0
  75. hpcflow/sdk/config/__init__.py +5 -0
  76. hpcflow/sdk/config/callbacks.py +246 -0
  77. hpcflow/sdk/config/cli.py +388 -0
  78. hpcflow/sdk/config/config.py +1410 -0
  79. hpcflow/sdk/config/config_file.py +501 -0
  80. hpcflow/sdk/config/errors.py +272 -0
  81. hpcflow/sdk/config/types.py +150 -0
  82. hpcflow/sdk/core/__init__.py +38 -0
  83. hpcflow/sdk/core/actions.py +3857 -0
  84. hpcflow/sdk/core/app_aware.py +25 -0
  85. hpcflow/sdk/core/cache.py +224 -0
  86. hpcflow/sdk/core/command_files.py +814 -0
  87. hpcflow/sdk/core/commands.py +424 -0
  88. hpcflow/sdk/core/element.py +2071 -0
  89. hpcflow/sdk/core/enums.py +221 -0
  90. hpcflow/sdk/core/environment.py +256 -0
  91. hpcflow/sdk/core/errors.py +1043 -0
  92. hpcflow/sdk/core/execute.py +207 -0
  93. hpcflow/sdk/core/json_like.py +809 -0
  94. hpcflow/sdk/core/loop.py +1320 -0
  95. hpcflow/sdk/core/loop_cache.py +282 -0
  96. hpcflow/sdk/core/object_list.py +933 -0
  97. hpcflow/sdk/core/parameters.py +3371 -0
  98. hpcflow/sdk/core/rule.py +196 -0
  99. hpcflow/sdk/core/run_dir_files.py +57 -0
  100. hpcflow/sdk/core/skip_reason.py +7 -0
  101. hpcflow/sdk/core/task.py +3792 -0
  102. hpcflow/sdk/core/task_schema.py +993 -0
  103. hpcflow/sdk/core/test_utils.py +538 -0
  104. hpcflow/sdk/core/types.py +447 -0
  105. hpcflow/sdk/core/utils.py +1207 -0
  106. hpcflow/sdk/core/validation.py +87 -0
  107. hpcflow/sdk/core/values.py +477 -0
  108. hpcflow/sdk/core/workflow.py +4820 -0
  109. hpcflow/sdk/core/zarr_io.py +206 -0
  110. hpcflow/sdk/data/__init__.py +13 -0
  111. hpcflow/sdk/data/config_file_schema.yaml +34 -0
  112. hpcflow/sdk/data/config_schema.yaml +260 -0
  113. hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
  114. hpcflow/sdk/data/files_spec_schema.yaml +5 -0
  115. hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
  116. hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
  117. hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
  118. hpcflow/sdk/demo/__init__.py +3 -0
  119. hpcflow/sdk/demo/cli.py +242 -0
  120. hpcflow/sdk/helper/__init__.py +3 -0
  121. hpcflow/sdk/helper/cli.py +137 -0
  122. hpcflow/sdk/helper/helper.py +300 -0
  123. hpcflow/sdk/helper/watcher.py +192 -0
  124. hpcflow/sdk/log.py +288 -0
  125. hpcflow/sdk/persistence/__init__.py +18 -0
  126. hpcflow/sdk/persistence/base.py +2817 -0
  127. hpcflow/sdk/persistence/defaults.py +6 -0
  128. hpcflow/sdk/persistence/discovery.py +39 -0
  129. hpcflow/sdk/persistence/json.py +954 -0
  130. hpcflow/sdk/persistence/pending.py +948 -0
  131. hpcflow/sdk/persistence/store_resource.py +203 -0
  132. hpcflow/sdk/persistence/types.py +309 -0
  133. hpcflow/sdk/persistence/utils.py +73 -0
  134. hpcflow/sdk/persistence/zarr.py +2388 -0
  135. hpcflow/sdk/runtime.py +320 -0
  136. hpcflow/sdk/submission/__init__.py +3 -0
  137. hpcflow/sdk/submission/enums.py +70 -0
  138. hpcflow/sdk/submission/jobscript.py +2379 -0
  139. hpcflow/sdk/submission/schedulers/__init__.py +281 -0
  140. hpcflow/sdk/submission/schedulers/direct.py +233 -0
  141. hpcflow/sdk/submission/schedulers/sge.py +376 -0
  142. hpcflow/sdk/submission/schedulers/slurm.py +598 -0
  143. hpcflow/sdk/submission/schedulers/utils.py +25 -0
  144. hpcflow/sdk/submission/shells/__init__.py +52 -0
  145. hpcflow/sdk/submission/shells/base.py +229 -0
  146. hpcflow/sdk/submission/shells/bash.py +504 -0
  147. hpcflow/sdk/submission/shells/os_version.py +115 -0
  148. hpcflow/sdk/submission/shells/powershell.py +352 -0
  149. hpcflow/sdk/submission/submission.py +1402 -0
  150. hpcflow/sdk/submission/types.py +140 -0
  151. hpcflow/sdk/typing.py +194 -0
  152. hpcflow/sdk/utils/arrays.py +69 -0
  153. hpcflow/sdk/utils/deferred_file.py +55 -0
  154. hpcflow/sdk/utils/hashing.py +16 -0
  155. hpcflow/sdk/utils/patches.py +31 -0
  156. hpcflow/sdk/utils/strings.py +69 -0
  157. hpcflow/tests/api/test_api.py +32 -0
  158. hpcflow/tests/conftest.py +123 -0
  159. hpcflow/tests/data/__init__.py +0 -0
  160. hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
  161. hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
  162. hpcflow/tests/data/multi_path_sequences.yaml +29 -0
  163. hpcflow/tests/data/workflow_1.json +10 -0
  164. hpcflow/tests/data/workflow_1.yaml +5 -0
  165. hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
  166. hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
  167. hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
  168. hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
  169. hpcflow/tests/programs/test_programs.py +180 -0
  170. hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
  171. hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
  172. hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
  173. hpcflow/tests/scripts/test_input_file_generators.py +282 -0
  174. hpcflow/tests/scripts/test_main_scripts.py +1361 -0
  175. hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
  176. hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
  177. hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
  178. hpcflow/tests/unit/test_action.py +1066 -0
  179. hpcflow/tests/unit/test_action_rule.py +24 -0
  180. hpcflow/tests/unit/test_app.py +132 -0
  181. hpcflow/tests/unit/test_cache.py +46 -0
  182. hpcflow/tests/unit/test_cli.py +172 -0
  183. hpcflow/tests/unit/test_command.py +377 -0
  184. hpcflow/tests/unit/test_config.py +195 -0
  185. hpcflow/tests/unit/test_config_file.py +162 -0
  186. hpcflow/tests/unit/test_element.py +666 -0
  187. hpcflow/tests/unit/test_element_iteration.py +88 -0
  188. hpcflow/tests/unit/test_element_set.py +158 -0
  189. hpcflow/tests/unit/test_group.py +115 -0
  190. hpcflow/tests/unit/test_input_source.py +1479 -0
  191. hpcflow/tests/unit/test_input_value.py +398 -0
  192. hpcflow/tests/unit/test_jobscript_unit.py +757 -0
  193. hpcflow/tests/unit/test_json_like.py +1247 -0
  194. hpcflow/tests/unit/test_loop.py +2674 -0
  195. hpcflow/tests/unit/test_meta_task.py +325 -0
  196. hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
  197. hpcflow/tests/unit/test_object_list.py +116 -0
  198. hpcflow/tests/unit/test_parameter.py +243 -0
  199. hpcflow/tests/unit/test_persistence.py +664 -0
  200. hpcflow/tests/unit/test_resources.py +243 -0
  201. hpcflow/tests/unit/test_run.py +286 -0
  202. hpcflow/tests/unit/test_run_directories.py +29 -0
  203. hpcflow/tests/unit/test_runtime.py +9 -0
  204. hpcflow/tests/unit/test_schema_input.py +372 -0
  205. hpcflow/tests/unit/test_shell.py +129 -0
  206. hpcflow/tests/unit/test_slurm.py +39 -0
  207. hpcflow/tests/unit/test_submission.py +502 -0
  208. hpcflow/tests/unit/test_task.py +2560 -0
  209. hpcflow/tests/unit/test_task_schema.py +182 -0
  210. hpcflow/tests/unit/test_utils.py +616 -0
  211. hpcflow/tests/unit/test_value_sequence.py +549 -0
  212. hpcflow/tests/unit/test_values.py +91 -0
  213. hpcflow/tests/unit/test_workflow.py +827 -0
  214. hpcflow/tests/unit/test_workflow_template.py +186 -0
  215. hpcflow/tests/unit/utils/test_arrays.py +40 -0
  216. hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
  217. hpcflow/tests/unit/utils/test_hashing.py +65 -0
  218. hpcflow/tests/unit/utils/test_patches.py +5 -0
  219. hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
  220. hpcflow/tests/unit/utils/test_strings.py +97 -0
  221. hpcflow/tests/workflows/__init__.py +0 -0
  222. hpcflow/tests/workflows/test_directory_structure.py +31 -0
  223. hpcflow/tests/workflows/test_jobscript.py +355 -0
  224. hpcflow/tests/workflows/test_run_status.py +198 -0
  225. hpcflow/tests/workflows/test_skip_downstream.py +696 -0
  226. hpcflow/tests/workflows/test_submission.py +140 -0
  227. hpcflow/tests/workflows/test_workflows.py +564 -0
  228. hpcflow/tests/workflows/test_zip.py +18 -0
  229. hpcflow/viz_demo.ipynb +6794 -0
  230. hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
  231. hpcflow-0.2.0a271.dist-info/METADATA +65 -0
  232. hpcflow-0.2.0a271.dist-info/RECORD +237 -0
  233. {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
  234. hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
  235. hpcflow/api.py +0 -458
  236. hpcflow/archive/archive.py +0 -308
  237. hpcflow/archive/cloud/cloud.py +0 -47
  238. hpcflow/archive/cloud/errors.py +0 -9
  239. hpcflow/archive/cloud/providers/dropbox.py +0 -432
  240. hpcflow/archive/errors.py +0 -5
  241. hpcflow/base_db.py +0 -4
  242. hpcflow/config.py +0 -232
  243. hpcflow/copytree.py +0 -66
  244. hpcflow/data/examples/_config.yml +0 -14
  245. hpcflow/data/examples/damask/demo/1.run.yml +0 -4
  246. hpcflow/data/examples/damask/demo/2.process.yml +0 -29
  247. hpcflow/data/examples/damask/demo/geom.geom +0 -2052
  248. hpcflow/data/examples/damask/demo/load.load +0 -1
  249. hpcflow/data/examples/damask/demo/material.config +0 -185
  250. hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
  251. hpcflow/data/examples/damask/inputs/load.load +0 -1
  252. hpcflow/data/examples/damask/inputs/material.config +0 -185
  253. hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
  254. hpcflow/data/examples/damask/profiles/damask.yml +0 -4
  255. hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
  256. hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
  257. hpcflow/data/examples/damask/profiles/default.yml +0 -6
  258. hpcflow/data/examples/thinking.yml +0 -177
  259. hpcflow/errors.py +0 -2
  260. hpcflow/init_db.py +0 -37
  261. hpcflow/models.py +0 -2549
  262. hpcflow/nesting.py +0 -9
  263. hpcflow/profiles.py +0 -455
  264. hpcflow/project.py +0 -81
  265. hpcflow/scheduler.py +0 -323
  266. hpcflow/utils.py +0 -103
  267. hpcflow/validation.py +0 -167
  268. hpcflow/variables.py +0 -544
  269. hpcflow-0.1.9.dist-info/METADATA +0 -168
  270. hpcflow-0.1.9.dist-info/RECORD +0 -45
  271. hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
  272. hpcflow-0.1.9.dist-info/top_level.txt +0 -1
  273. /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
  274. /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
  275. /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ import pytest
4
+ from hpcflow.app import app as hf
5
+
6
+
7
+ @pytest.mark.parametrize("store", ["json", "zarr"])
8
+ def test_decode(null_config, tmp_path: Path, store: str):
9
+ s1 = hf.TaskSchema(
10
+ objective="t1",
11
+ inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"))],
12
+ outputs=[hf.SchemaInput(parameter=hf.Parameter("p2"))],
13
+ actions=[
14
+ hf.Action(
15
+ commands=[
16
+ hf.Command(
17
+ command="Write-Output (<<parameter:p1>> + 100)",
18
+ stdout="<<parameter:p2>>",
19
+ )
20
+ ]
21
+ )
22
+ ],
23
+ )
24
+ wk = hf.Workflow.from_template_data(
25
+ tasks=[hf.Task(schema=s1, inputs=[hf.InputValue("p1", value=101)])],
26
+ loops=[hf.Loop(tasks=[0], num_iterations=1)],
27
+ path=tmp_path,
28
+ template_name="wk0",
29
+ store=store,
30
+ )
31
+ iter_i = wk.tasks[0].elements[0].iterations[0]
32
+ assert iter_i.id_ == 0
33
+ assert iter_i.index == 0
34
+ assert iter_i.EARs_initialised == True
35
+ assert sorted(iter_i.data_idx) == sorted(
36
+ {"inputs.p1": 2, "resources.any": 1, "outputs.p2": 3}
37
+ )
38
+ assert iter_i.loop_idx == {"loop_0": 0}
39
+ assert sorted(iter_i.schema_parameters) == sorted(
40
+ ["resources.any", "inputs.p1", "outputs.p2"]
41
+ )
42
+
43
+
44
+ @pytest.mark.integration
45
+ def test_loop_skipped_true_single_action_elements(null_config, tmp_path):
46
+ ts = hf.TaskSchema(
47
+ objective="t1",
48
+ inputs=[hf.SchemaInput("p1")],
49
+ outputs=[hf.SchemaInput("p1")],
50
+ actions=[
51
+ hf.Action(
52
+ commands=[
53
+ hf.Command(
54
+ command="echo $(( <<parameter:p1>> + 100 ))",
55
+ stdout="<<int(parameter:p1)>>",
56
+ ),
57
+ ]
58
+ ),
59
+ ],
60
+ )
61
+ loop_term = hf.Rule(path="outputs.p1", condition={"value.equal_to": 300})
62
+ wk = hf.Workflow.from_template_data(
63
+ template_name="test_loop_skipped",
64
+ path=tmp_path,
65
+ tasks=[hf.Task(schema=ts, inputs={"p1": 100})],
66
+ loops=[
67
+ hf.Loop(name="my_loop", tasks=[0], termination=loop_term, num_iterations=3)
68
+ ],
69
+ )
70
+ # loop should terminate after the second iteration; third iteration should
71
+ # be loop-skipped
72
+ wk.submit(wait=True, add_to_known=False, status=False)
73
+ iters = wk.get_all_element_iterations()
74
+
75
+ assert not iters[0].loop_skipped
76
+ assert not iters[1].loop_skipped
77
+ assert iters[2].loop_skipped
78
+
79
+ # check latest iteration is the latest non-loop-skipped iteration:
80
+ assert wk.tasks[0].elements[0].latest_iteration_non_skipped.id_ == iters[1].id_
81
+
82
+ # check element inputs are from latest non-loop-skipped iteration:
83
+ assert wk.tasks[0].elements[0].inputs.p1.value == 200
84
+ assert wk.tasks[0].elements[0].get("inputs.p1") == 200
85
+
86
+ # check element outputs are from latest non-loop-skipped iteration:
87
+ assert wk.tasks[0].elements[0].outputs.p1.value == 300
88
+ assert wk.tasks[0].elements[0].get("outputs.p1") == 300
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+ import pytest
4
+ from hpcflow.app import app as hf
5
+ from hpcflow.sdk.core.errors import MalformedNestingOrderPath
6
+
7
+ if TYPE_CHECKING:
8
+ from pathlib import Path
9
+ from hpcflow.sdk.core.parameters import Parameter
10
+ from hpcflow.sdk.core.types import ResourceSpecArgs
11
+ from hpcflow.sdk.core.workflow import Workflow
12
+
13
+
14
+ @pytest.fixture
15
+ def null_config(tmp_path: Path):
16
+ if not hf.is_config_loaded:
17
+ hf.load_config(config_dir=tmp_path)
18
+
19
+
20
+ @pytest.fixture
21
+ def param_p1() -> Parameter:
22
+ return hf.Parameter("p1")
23
+
24
+
25
+ @pytest.fixture
26
+ def param_p2() -> Parameter:
27
+ return hf.Parameter("p2")
28
+
29
+
30
+ @pytest.fixture
31
+ def param_p3() -> Parameter:
32
+ return hf.Parameter("p3")
33
+
34
+
35
+ @pytest.fixture
36
+ def workflow_w1(
37
+ null_config, tmp_path: Path, param_p1: Parameter, param_p2: Parameter
38
+ ) -> Workflow:
39
+ s1 = hf.TaskSchema("t1", actions=[], inputs=[param_p1], outputs=[param_p2])
40
+ s2 = hf.TaskSchema("t2", actions=[], inputs=[param_p2])
41
+
42
+ t1 = hf.Task(
43
+ schema=s1,
44
+ sequences=[hf.ValueSequence("inputs.p1", values=[101, 102], nesting_order=1)],
45
+ )
46
+ t2 = hf.Task(schema=s2, nesting_order={"inputs.p2": 1})
47
+
48
+ wkt = hf.WorkflowTemplate(name="w1", tasks=[t1, t2])
49
+ return hf.Workflow.from_template(wkt, path=tmp_path)
50
+
51
+
52
+ @pytest.fixture
53
+ def workflow_w2(workflow_w1: Workflow) -> Workflow:
54
+ """Add another element set to the second task."""
55
+ workflow_w1.tasks.t2.add_elements(nesting_order={"inputs.p2": 1})
56
+ return workflow_w1
57
+
58
+
59
+ def test_resources_init_equivalence_dict_list_of_obj() -> None:
60
+ es1 = hf.ElementSet(resources={"any": {"num_cores": 1}})
61
+ es2 = hf.ElementSet(resources=[hf.ResourceSpec(scope="any", num_cores=1)])
62
+ assert es1 == es2
63
+
64
+
65
+ def test_resources_init_equivalence_list_list_of_obj() -> None:
66
+ res_1_kwargs: ResourceSpecArgs = {"scope": "any", "num_cores": 1}
67
+ es1 = hf.ElementSet(resources=[res_1_kwargs])
68
+ es2 = hf.ElementSet(resources=[hf.ResourceSpec(**res_1_kwargs)])
69
+ assert es1 == es2
70
+
71
+
72
+ def test_resources_init_equivalence_list_of_obj_resource_list_obj() -> None:
73
+ res_1_kwargs: ResourceSpecArgs = {"scope": "any", "num_cores": 1}
74
+ es1 = hf.ElementSet(resources=[hf.ResourceSpec(**res_1_kwargs)])
75
+ es2 = hf.ElementSet(resources=hf.ResourceList([hf.ResourceSpec(**res_1_kwargs)]))
76
+ assert es1 == es2
77
+
78
+
79
+ def test_repeats_single_int_equivalence(null_config) -> None:
80
+ es1 = hf.ElementSet(repeats=2)
81
+ es2 = hf.ElementSet(repeats=[{"name": "", "number": 2, "nesting_order": 0}])
82
+ assert es1 == es2
83
+
84
+
85
+ def test_merge_envs(null_config) -> None:
86
+ envs = {"my_env": {"version": "1.0"}}
87
+ es = hf.ElementSet(environments=envs)
88
+ assert es.resources.get(scope=hf.ActionScope.any()).environments == envs
89
+
90
+
91
+ def test_merge_envs_existing_any_resources(null_config) -> None:
92
+ envs = {"my_env": {"version": "1.0"}}
93
+ num_cores = 2
94
+ es = hf.ElementSet(resources={"any": {"num_cores": num_cores}}, environments=envs)
95
+ assert es.resources.get(scope=hf.ActionScope.any()).environments == envs
96
+ assert es.resources.get(scope=hf.ActionScope.any()).num_cores == num_cores
97
+
98
+
99
+ def test_merge_envs_resource_envs_precedence(null_config) -> None:
100
+ envs = {"my_env": {"version": "1.0"}}
101
+ res_envs = {"other_env": {"version": "2.0"}}
102
+ es = hf.ElementSet(resources={"any": {"environments": res_envs}}, environments=envs)
103
+ assert es.resources.get(scope=hf.ActionScope.any()).environments == res_envs
104
+
105
+
106
+ def test_merge_envs_no_envs_with_resource_envs(null_config) -> None:
107
+ envs = {"my_env": {"version": "1.0"}}
108
+ es = hf.ElementSet(resources={"any": {"environments": envs}})
109
+ assert es.resources.get(scope=hf.ActionScope.any()).environments == envs
110
+
111
+
112
+ def test_raise_env_and_envs_specified(null_config) -> None:
113
+ with pytest.raises(ValueError):
114
+ hf.ElementSet(env_preset="my_preset", environments={"my_env": {"version": 1}})
115
+
116
+
117
+ def test_nesting_order_paths_raise(null_config) -> None:
118
+ with pytest.raises(MalformedNestingOrderPath):
119
+ hf.ElementSet(nesting_order={"bad_path.p1": 1})
120
+
121
+
122
+ def test_nesting_order_paths_no_raise(null_config) -> None:
123
+ hf.ElementSet(nesting_order={"inputs.p1": 1, "resources.any": 2, "repeats": 3})
124
+
125
+
126
+ def test_input_source_str_dict_list_str_list_dict_equivalence(null_config) -> None:
127
+ inp_source_dict: dict[str, str | int] = {
128
+ "source_type": "task",
129
+ "task_source_type": "output",
130
+ "task_ref": 0,
131
+ }
132
+ inp_source_str = "task.0.output"
133
+ inp_source_list_dict = [inp_source_dict]
134
+ inp_source_list_str = [inp_source_str]
135
+ assert (
136
+ hf.ElementSet.from_json_like(
137
+ {"input_sources": {"p1": inp_source_dict}}
138
+ ).input_sources
139
+ == hf.ElementSet.from_json_like(
140
+ {"input_sources": {"p1": inp_source_list_dict}}
141
+ ).input_sources
142
+ == hf.ElementSet.from_json_like(
143
+ {"input_sources": {"p1": inp_source_str}}
144
+ ).input_sources
145
+ == hf.ElementSet.from_json_like(
146
+ {"input_sources": {"p1": inp_source_list_str}}
147
+ ).input_sources
148
+ )
149
+
150
+
151
+ def test_element_set_input_dict_equivalence(null_config):
152
+ assert hf.ElementSet(
153
+ inputs=[hf.InputValue("p1", label="A", value=1)]
154
+ ) == hf.ElementSet(inputs={"p1[A]": 1})
155
+
156
+ assert hf.ElementSet(
157
+ inputs=[hf.InputValue("p1", label="A", path="b", value=1)]
158
+ ) == hf.ElementSet(inputs={"p1[A].b": 1})
@@ -0,0 +1,115 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ import pytest
4
+ from hpcflow.app import app as hf
5
+ from hpcflow.sdk.core.errors import MissingElementGroup
6
+
7
+
8
+ def test_group_simple(null_config, tmp_path: Path):
9
+ s1 = hf.TaskSchema(
10
+ objective="t1",
11
+ inputs=[hf.SchemaInput("p1")],
12
+ outputs=[hf.SchemaOutput("p2")],
13
+ actions=[
14
+ hf.Action(
15
+ commands=[
16
+ hf.Command(
17
+ "echo $(( <<parameter:p1>> + 1 ))", stdout="<<parameter:p2>>"
18
+ )
19
+ ]
20
+ )
21
+ ],
22
+ )
23
+ s2 = hf.TaskSchema(
24
+ objective="t2",
25
+ inputs=[hf.SchemaInput("p2", group="my_group")],
26
+ outputs=[hf.SchemaOutput("p3")],
27
+ actions=[
28
+ hf.Action(
29
+ commands=[
30
+ hf.Command(
31
+ "echo $(( <<parameter:p2>> + 2 ))", stdout="<<parameter:p3>>"
32
+ )
33
+ ]
34
+ )
35
+ ],
36
+ )
37
+
38
+ t1 = hf.Task(
39
+ schema=s1,
40
+ sequences=[hf.ValueSequence("inputs.p1", values=[1, 2, 3])],
41
+ groups=[hf.ElementGroup(name="my_group")],
42
+ )
43
+ t2 = hf.Task(schema=s2)
44
+ wk = hf.Workflow.from_template_data(
45
+ template_name="test_groups",
46
+ path=tmp_path,
47
+ tasks=[t1, t2],
48
+ )
49
+ assert [task.num_elements for task in wk.tasks] == [3, 1]
50
+ assert len(wk.tasks.t2.elements[0].get_data_idx("inputs.p2")["inputs.p2"]) == 3
51
+
52
+
53
+ def test_group_raise_no_elements(null_config, tmp_path: Path):
54
+ s1 = hf.TaskSchema(
55
+ objective="t1",
56
+ inputs=[hf.SchemaInput("p1")],
57
+ outputs=[hf.SchemaOutput("p2")],
58
+ actions=[
59
+ hf.Action(
60
+ commands=[
61
+ hf.Command(
62
+ "echo $(( <<parameter:p1>> + 1 ))", stdout="<<parameter:p2>>"
63
+ )
64
+ ]
65
+ )
66
+ ],
67
+ )
68
+ s2 = hf.TaskSchema(
69
+ objective="t2",
70
+ inputs=[hf.SchemaInput("p2", group="my_group")],
71
+ outputs=[hf.SchemaOutput("p3")],
72
+ actions=[
73
+ hf.Action(
74
+ commands=[
75
+ hf.Command(
76
+ "echo $(( <<parameter:p2>> + 2 ))", stdout="<<parameter:p3>>"
77
+ )
78
+ ]
79
+ )
80
+ ],
81
+ )
82
+
83
+ t1 = hf.Task(
84
+ schema=s1,
85
+ sequences=[hf.ValueSequence("inputs.p1", values=[1, 2, 3])],
86
+ )
87
+ t2 = hf.Task(schema=s2)
88
+ with pytest.raises(MissingElementGroup):
89
+ hf.Workflow.from_template_data(
90
+ template_name="test_groups",
91
+ path=tmp_path,
92
+ tasks=[t1, t2],
93
+ )
94
+
95
+
96
+ def test_group_on_input_only_task(null_config, tmp_path: Path):
97
+
98
+ s1 = hf.TaskSchema(objective="t1", inputs=[hf.SchemaInput("p1")])
99
+ s2 = hf.TaskSchema(objective="t2", inputs=[hf.SchemaInput("p1", group="all")])
100
+
101
+ t1 = hf.Task(
102
+ schema=s1,
103
+ inputs={"p1": 100},
104
+ repeats=2,
105
+ groups=[hf.ElementGroup(name="all")], # define a group on a task with no actions
106
+ )
107
+ t2 = hf.Task(schema=s2)
108
+
109
+ wk = hf.Workflow.from_template_data(
110
+ template_name="test_input_group",
111
+ path=tmp_path,
112
+ tasks=[t1, t2],
113
+ )
114
+ assert wk.tasks.t1.num_elements == 2
115
+ assert wk.tasks.t2.num_elements == 1