hpcflow 0.1.15__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 -461
  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.15.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 -490
  236. hpcflow/archive/archive.py +0 -307
  237. hpcflow/archive/cloud/cloud.py +0 -45
  238. hpcflow/archive/cloud/errors.py +0 -9
  239. hpcflow/archive/cloud/providers/dropbox.py +0 -427
  240. hpcflow/archive/errors.py +0 -5
  241. hpcflow/base_db.py +0 -4
  242. hpcflow/config.py +0 -233
  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 -2595
  262. hpcflow/nesting.py +0 -9
  263. hpcflow/profiles.py +0 -455
  264. hpcflow/project.py +0 -81
  265. hpcflow/scheduler.py +0 -322
  266. hpcflow/utils.py +0 -103
  267. hpcflow/validation.py +0 -166
  268. hpcflow/variables.py +0 -543
  269. hpcflow-0.1.15.dist-info/METADATA +0 -168
  270. hpcflow-0.1.15.dist-info/RECORD +0 -45
  271. hpcflow-0.1.15.dist-info/entry_points.txt +0 -8
  272. hpcflow-0.1.15.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,377 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ from typing import Any, Sequence, TYPE_CHECKING
4
+ import numpy as np
5
+ import pytest
6
+ from hpcflow.app import app as hf
7
+ from hpcflow.sdk.core.test_utils import (
8
+ P1_parameter_cls as P1,
9
+ P1_sub_parameter_cls as P1_sub,
10
+ command_line_test,
11
+ )
12
+
13
+
14
+ def test_get_command_line(null_config, tmp_path: Path):
15
+ p1_value = 1
16
+ cmd_str = "Write-Output (<<parameter:p1>> + 100)"
17
+ expected = f"Write-Output ({p1_value} + 100)"
18
+ command_line_test(cmd_str, expected, {"p1": p1_value}, tmp_path)
19
+
20
+
21
+ @pytest.mark.parametrize("shell_args", [("powershell", "nt"), ("bash", "posix")])
22
+ def test_get_command_line_with_stdout(
23
+ null_config, tmp_path: Path, shell_args: tuple[str, str]
24
+ ):
25
+ p1_value = 1
26
+ expected = {
27
+ ("powershell", "nt"): f"$parameter_p2 = Write-Output ({p1_value} + 100)",
28
+ ("bash", "posix"): f"parameter_p2=`Write-Output ({p1_value} + 100)`",
29
+ }
30
+ command_line_test(
31
+ cmd_str="Write-Output (<<parameter:p1>> + 100)",
32
+ cmd_stdout="<<parameter:p2>>",
33
+ expected=expected[shell_args],
34
+ inputs={"p1": p1_value},
35
+ outputs=("p2",),
36
+ shell_args=shell_args,
37
+ path=tmp_path,
38
+ )
39
+
40
+
41
+ def test_get_command_line_single_labelled_input(null_config, tmp_path: Path):
42
+ p1_value = 1
43
+ command_line_test(
44
+ cmd_str="Write-Output (<<parameter:p1[one]>> + 100)",
45
+ expected=f"Write-Output ({p1_value} + 100)",
46
+ schema_inputs=[hf.SchemaInput(parameter=hf.Parameter("p1"), labels={"one": {}})],
47
+ inputs=[hf.InputValue("p1", label="one", value=p1_value)],
48
+ path=tmp_path,
49
+ )
50
+
51
+
52
+ def test_get_command_line_multiple_labelled_input(null_config, tmp_path: Path):
53
+ p1_one_value = 1
54
+ p1_two_value = 2
55
+ command_line_test(
56
+ cmd_str="Write-Output (<<parameter:p1[one]>> + <<parameter:p1[two]>> + 100)",
57
+ expected=f"Write-Output ({p1_one_value} + {p1_two_value} + 100)",
58
+ schema_inputs=[
59
+ hf.SchemaInput(
60
+ parameter=hf.Parameter("p1"), multiple=True, labels={"one": {}, "two": {}}
61
+ )
62
+ ],
63
+ inputs=[
64
+ hf.InputValue("p1", label="one", value=p1_one_value),
65
+ hf.InputValue("p1", label="two", value=p1_two_value),
66
+ ],
67
+ path=tmp_path,
68
+ )
69
+
70
+
71
+ def test_get_command_line_sub_parameter(null_config, tmp_path: Path):
72
+ p1_value = {"a": 1}
73
+ command_line_test(
74
+ cmd_str="Write-Output (<<parameter:p1.a>> + 100)",
75
+ expected=f"Write-Output ({p1_value['a']} + 100)",
76
+ inputs={"p1": p1_value},
77
+ path=tmp_path,
78
+ )
79
+
80
+
81
+ def test_get_command_line_sum(null_config, tmp_path: Path):
82
+ p1_value = [1, 2, 3]
83
+ command_line_test(
84
+ cmd_str="Write-Output (<<sum(parameter:p1)>> + 100)",
85
+ expected=f"Write-Output ({sum(p1_value)} + 100)",
86
+ inputs={"p1": p1_value},
87
+ path=tmp_path,
88
+ )
89
+
90
+
91
+ def test_get_command_line_join(null_config, tmp_path: Path):
92
+ p1_value = [1, 2, 3]
93
+ delim = ","
94
+ command_line_test(
95
+ cmd_str=f'Write-Output (<<join[delim="{delim}"](parameter:p1)>> + 100)',
96
+ expected=f"Write-Output ({delim.join(str(i) for i in p1_value)} + 100)",
97
+ inputs={"p1": p1_value},
98
+ path=tmp_path,
99
+ )
100
+
101
+
102
+ def test_get_command_line_sum_sub_data(null_config, tmp_path: Path):
103
+ p1_value = {"a": [1, 2, 3]}
104
+ command_line_test(
105
+ cmd_str="Write-Output (<<sum(parameter:p1.a)>> + 100)",
106
+ expected=f"Write-Output ({sum(p1_value['a'])} + 100)",
107
+ inputs={"p1": p1_value},
108
+ path=tmp_path,
109
+ )
110
+
111
+
112
+ def test_get_command_line_join_sub_data(null_config, tmp_path):
113
+ delim = ","
114
+ p1_value = {"a": [1, 2, 3]}
115
+ command_line_test(
116
+ cmd_str=f'Write-Output (<<join[delim="{delim}"](parameter:p1.a)>> + 100)',
117
+ expected=f"Write-Output ({delim.join(str(i) for i in p1_value['a'])} + 100)",
118
+ inputs={"p1": p1_value},
119
+ path=tmp_path,
120
+ )
121
+
122
+
123
+ def test_get_command_line_parameter_value(null_config, tmp_path: Path):
124
+ p1_value = P1(a=1) # has a `CLI_format` method defined which returns `str(a)`
125
+ command_line_test(
126
+ cmd_str="Write-Output (<<parameter:p1c>> + 100)",
127
+ expected=f"Write-Output ({p1_value.a} + 100)",
128
+ inputs={"p1c": p1_value},
129
+ path=tmp_path,
130
+ )
131
+
132
+
133
+ def test_get_command_line_parameter_value_join(null_config, tmp_path: Path):
134
+ delim = ","
135
+ p1_value = P1(a=4)
136
+ command_line_test(
137
+ cmd_str=(
138
+ f"Write-Output "
139
+ f'<<join[delim="{delim}"](parameter:p1c.custom_CLI_format_prep(reps=4))>>'
140
+ ),
141
+ expected="Write-Output 4,4,4,4",
142
+ inputs={"p1c": p1_value},
143
+ path=tmp_path,
144
+ )
145
+
146
+
147
+ def test_get_command_line_parameter_value_custom_method(null_config, tmp_path: Path):
148
+ p1_value = P1(a=1)
149
+ command_line_test(
150
+ cmd_str="Write-Output (<<parameter:p1c.custom_CLI_format()>> + 100)",
151
+ expected=f"Write-Output ({p1_value.a + 4} + 100)",
152
+ inputs={"p1c": p1_value},
153
+ path=tmp_path,
154
+ )
155
+
156
+
157
+ def test_get_command_line_parameter_value_custom_method_with_args(
158
+ null_config, tmp_path: Path
159
+ ):
160
+ p1_value = P1(a=1)
161
+ add_val = 35
162
+ command_line_test(
163
+ cmd_str=f"Write-Output (<<parameter:p1c.custom_CLI_format(add={add_val})>> + 100)",
164
+ expected=f"Write-Output ({p1_value.a + add_val} + 100)",
165
+ inputs={"p1c": p1_value},
166
+ path=tmp_path,
167
+ )
168
+
169
+
170
+ def test_get_command_line_parameter_value_custom_method_with_two_args(
171
+ null_config, tmp_path: Path
172
+ ):
173
+ add_val = 35
174
+ sub_val = 10
175
+ p1_value = P1(a=1)
176
+ command_line_test(
177
+ cmd_str=(
178
+ f"Write-Output ("
179
+ f"<<parameter:p1c.custom_CLI_format(add={add_val}, sub={sub_val})>> + 100)"
180
+ ),
181
+ expected=f"Write-Output ({p1_value.a + add_val - sub_val} + 100)",
182
+ inputs={"p1c": p1_value},
183
+ path=tmp_path,
184
+ )
185
+
186
+
187
+ def test_get_command_line_parameter_value_sub_object(null_config, tmp_path: Path):
188
+ p1_value = P1(a=1, sub_param=P1_sub(e=5))
189
+ assert p1_value.sub_param
190
+ command_line_test(
191
+ cmd_str=f"Write-Output (<<parameter:p1c.sub_param>> + 100)",
192
+ expected=f"Write-Output ({p1_value.sub_param.e} + 100)",
193
+ inputs={"p1c": p1_value},
194
+ path=tmp_path,
195
+ )
196
+
197
+
198
+ def test_get_command_line_parameter_value_sub_object_attr(null_config, tmp_path: Path):
199
+ p1_value = P1(a=1, sub_param=P1_sub(e=5))
200
+ assert p1_value.sub_param
201
+ command_line_test(
202
+ cmd_str=f"Write-Output (" f"<<parameter:p1c.sub_param.e>> + 100)",
203
+ expected=f"Write-Output ({p1_value.sub_param.e} + 100)",
204
+ inputs={"p1c": p1_value},
205
+ path=tmp_path,
206
+ )
207
+
208
+
209
+ def test_process_std_stream_int(null_config) -> None:
210
+ cmd = hf.Command(command="", stdout="<<int(parameter:p2)>>")
211
+ assert cmd.process_std_stream(name="p2", value="101", stderr=False) == 101
212
+
213
+
214
+ def test_process_std_stream_stderr_int(null_config) -> None:
215
+ cmd = hf.Command(command="", stderr="<<int(parameter:p2)>>")
216
+ assert cmd.process_std_stream(name="p2", value="101", stderr=True) == 101
217
+
218
+
219
+ def test_process_std_stream_float(null_config) -> None:
220
+ cmd = hf.Command(command="", stdout="<<float(parameter:p2)>>")
221
+ assert cmd.process_std_stream(name="p2", value="3.1415", stderr=False) == 3.1415
222
+
223
+
224
+ def test_process_std_stream_bool_true(null_config) -> None:
225
+ cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
226
+ for value in ("true", "True", "1"):
227
+ assert cmd.process_std_stream(name="p2", value=value, stderr=False) == True
228
+
229
+
230
+ def test_process_std_stream_bool_false(null_config) -> None:
231
+ cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
232
+ for value in ("false", "False", "0"):
233
+ assert cmd.process_std_stream(name="p2", value=value, stderr=False) == False
234
+
235
+
236
+ def test_process_std_stream_bool_raise(null_config) -> None:
237
+ cmd = hf.Command(command="", stdout="<<bool(parameter:p2)>>")
238
+ for value in ("hi", "120", "-1"):
239
+ with pytest.raises(ValueError):
240
+ cmd.process_std_stream(name="p2", value=value, stderr=False)
241
+
242
+
243
+ def test_process_std_stream_list(null_config) -> None:
244
+ cmd = hf.Command(command="", stdout="<<list(parameter:p2)>>")
245
+ assert cmd.process_std_stream(name="p2", value="1 2 3", stderr=False) == [
246
+ "1",
247
+ "2",
248
+ "3",
249
+ ]
250
+
251
+
252
+ def test_process_std_stream_list_int(null_config) -> None:
253
+ cmd = hf.Command(command="", stdout="<<list[item_type=int](parameter:p2)>>")
254
+ assert cmd.process_std_stream(name="p2", value="1 2 3", stderr=False) == [1, 2, 3]
255
+
256
+
257
+ def test_process_std_stream_list_delim(null_config) -> None:
258
+ cmd = hf.Command(command="", stdout='<<list[delim=","](parameter:p2)>>')
259
+ assert cmd.process_std_stream(name="p2", value="1,2,3", stderr=False) == [
260
+ "1",
261
+ "2",
262
+ "3",
263
+ ]
264
+
265
+
266
+ def test_process_std_stream_list_int_delim(null_config) -> None:
267
+ cmd = hf.Command(
268
+ command="", stdout='<<list[item_type=int, delim=","](parameter:p2)>>'
269
+ )
270
+ assert cmd.process_std_stream(name="p2", value="1,2,3", stderr=False) == [1, 2, 3]
271
+
272
+
273
+ def test_process_std_stream_list_float_delim_colon(null_config) -> None:
274
+ cmd = hf.Command(
275
+ command="", stdout='<<list[item_type=float, delim=":"](parameter:p2)>>'
276
+ )
277
+ assert cmd.process_std_stream(name="p2", value="1.1:2.2:3.3", stderr=False) == [
278
+ 1.1,
279
+ 2.2,
280
+ 3.3,
281
+ ]
282
+
283
+
284
+ def test_process_std_stream_array(null_config) -> None:
285
+ cmd = hf.Command(command="", stdout="<<array(parameter:p2)>>")
286
+ assert np.allclose(
287
+ cmd.process_std_stream(name="p2", value="1 2 3", stderr=False),
288
+ np.array([1, 2, 3]),
289
+ )
290
+
291
+
292
+ def test_process_std_stream_array_delim(null_config) -> None:
293
+ cmd = hf.Command(command="", stdout='<<array[delim=","](parameter:p2)>>')
294
+ assert np.allclose(
295
+ cmd.process_std_stream(name="p2", value="1,2,3", stderr=False),
296
+ np.array([1, 2, 3]),
297
+ )
298
+
299
+
300
+ def test_process_std_stream_array_dtype_int(null_config) -> None:
301
+ cmd = hf.Command(command="", stdout="<<array[item_type=int](parameter:p2)>>")
302
+ arr = cmd.process_std_stream(name="p2", value="1 2 3", stderr=False)
303
+ assert arr.dtype == np.dtype("int")
304
+
305
+
306
+ def test_process_std_stream_array_dtype_float(null_config) -> None:
307
+ cmd = hf.Command(command="", stdout="<<array[item_type=float](parameter:p2)>>")
308
+ arr = cmd.process_std_stream(name="p2", value="1 2 3", stderr=False)
309
+ assert arr.dtype == np.dtype("float")
310
+
311
+
312
+ def test_process_std_stream_object(null_config) -> None:
313
+ cmd = hf.Command(command="", stdout="<<parameter:p1c>>")
314
+ a_val = 12
315
+ assert cmd.process_std_stream(name="p1c", value=str(a_val), stderr=False) == P1(
316
+ a=a_val
317
+ )
318
+
319
+
320
+ def test_process_std_stream_object_kwargs(null_config) -> None:
321
+ cmd = hf.Command(command="", stdout="<<parameter:p1c.CLI_parse(double=true)>>")
322
+ a_val = 12
323
+ expected = 2 * a_val
324
+ assert cmd.process_std_stream(name="p1c", value=str(a_val), stderr=False) == P1(
325
+ a=expected
326
+ )
327
+
328
+
329
+ def test_get_output_types(null_config) -> None:
330
+ cmd = hf.Command(command="", stdout="<<parameter:p1_test_123>>")
331
+ assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
332
+
333
+
334
+ def test_get_output_types_int(null_config) -> None:
335
+ cmd = hf.Command(command="", stdout="<<int(parameter:p1_test_123)>>")
336
+ assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
337
+
338
+
339
+ def test_get_output_types_object_with_args(null_config) -> None:
340
+ cmd = hf.Command(
341
+ command="", stdout="<<parameter:p1_test_123.CLI_parse(double=true)>>"
342
+ )
343
+ assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
344
+
345
+
346
+ def test_get_output_types_list(null_config) -> None:
347
+ cmd = hf.Command(
348
+ command="", stdout="<<list[item_type=int, delim=" "](parameter:p1_test_123)>>"
349
+ )
350
+ assert cmd.get_output_types() == {"stdout": "p1_test_123", "stderr": None}
351
+
352
+
353
+ def test_get_output_types_no_match(null_config) -> None:
354
+ cmd = hf.Command(command="", stdout="parameter:p1_test_123")
355
+ assert cmd.get_output_types() == {"stdout": None, "stderr": None}
356
+
357
+
358
+ def test_get_output_types_raise_with_extra_substring_start(null_config) -> None:
359
+ cmd = hf.Command(command="", stdout="hello: <<parameter:p1_test_123>>")
360
+ with pytest.raises(ValueError):
361
+ cmd.get_output_types()
362
+
363
+
364
+ def test_get_output_types_raise_with_extra_substring_end(null_config) -> None:
365
+ cmd = hf.Command(command="", stdout="<<parameter:p1_test_123>> hello")
366
+ with pytest.raises(ValueError):
367
+ cmd.get_output_types()
368
+
369
+
370
+ def test_extract_executable_labels(null_config) -> None:
371
+ tests = {
372
+ "<<executable:m1>> and <<executable:12>>": ["m1", "12"],
373
+ "<<executable:m1>> hi": ["m1"],
374
+ "<<executable:m1": [],
375
+ }
376
+ for k, v in tests.items():
377
+ assert hf.Command._extract_executable_labels(k) == v
@@ -0,0 +1,195 @@
1
+ from __future__ import annotations
2
+ import os
3
+ import time
4
+ import pytest
5
+
6
+ from hpcflow.app import app as hf
7
+ from hpcflow.sdk.config.errors import (
8
+ ConfigFileValidationError,
9
+ ConfigItemCallbackError,
10
+ ConfigNonConfigurableError,
11
+ ConfigReadOnlyError,
12
+ )
13
+
14
+
15
+ def test_reset_config(new_null_config) -> None:
16
+ cfg_dir = hf.config.config_directory
17
+ machine_name = hf.config.machine
18
+ new_machine_name = machine_name + "123"
19
+ hf.config.machine = new_machine_name
20
+ assert hf.config.machine == new_machine_name
21
+ hf.reset_config(config_dir=cfg_dir)
22
+ assert hf.config.machine == machine_name
23
+
24
+
25
+ def test_raise_on_invalid_config_file(new_null_config) -> None:
26
+ # make an invalid config file:
27
+ cfg_path = hf.config.config_file_path
28
+ with cfg_path.open("at+") as f:
29
+ f.write("something_invalid: 1\n")
30
+
31
+ # try to load the invalid file:
32
+ cfg_dir = hf.config.config_directory
33
+ with pytest.raises(ConfigFileValidationError):
34
+ hf.reload_config(config_dir=cfg_dir, warn=False)
35
+ hf.reset_config(config_dir=cfg_dir, warn=False)
36
+ hf.unload_config()
37
+
38
+
39
+ def test_reset_invalid_config(new_null_config) -> None:
40
+ # make an invalid config file:
41
+ cfg_path = hf.config.config_file_path
42
+ with cfg_path.open("at+") as f:
43
+ f.write("something_invalid: 1\n")
44
+
45
+ # check we can reset the invalid file:
46
+ cfg_dir = hf.config.config_directory
47
+ hf.reset_config(config_dir=cfg_dir, warn=False)
48
+ hf.unload_config()
49
+
50
+
51
+ def test_raise_on_set_default_scheduler_not_in_schedulers_list_invalid_name(
52
+ null_config,
53
+ ) -> None:
54
+ new_default = "invalid-scheduler"
55
+ with pytest.raises(ConfigItemCallbackError):
56
+ hf.config.default_scheduler = new_default
57
+
58
+
59
+ def test_raise_on_set_default_scheduler_not_in_schedulers_list_valid_name(
60
+ null_config,
61
+ ) -> None:
62
+ new_default = "slurm" # valid but unsupported (by default) scheduler
63
+ with pytest.raises(ConfigItemCallbackError):
64
+ hf.config.default_scheduler = new_default
65
+
66
+
67
+ def test_without_callbacks_ctx_manager(null_config) -> None:
68
+ # set a new shell that would raise an error in the `callback_supported_shells`:
69
+ new_default = "bash" if os.name == "nt" else "powershell"
70
+
71
+ with hf.config._without_callbacks("callback_supported_shells"):
72
+ hf.config.default_shell = new_default
73
+ assert hf.config.default_shell == new_default
74
+
75
+ # outside the context manager, the callback is reinstated, which should raise:
76
+ with pytest.raises(ConfigItemCallbackError):
77
+ hf.config.default_shell
78
+
79
+ # unload the modified config so it's not reused by other tests
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()