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,24 @@
1
+ from __future__ import annotations
2
+ from typing import cast, TYPE_CHECKING
3
+ from hpcflow.app import app as hf
4
+
5
+ if TYPE_CHECKING:
6
+ from hpcflow.sdk.core.types import RuleArgs
7
+
8
+
9
+ def test_equivalent_init_with_rule_args() -> None:
10
+ rule_args: RuleArgs = {
11
+ "path": "resources.os_name",
12
+ "condition": {"value.equal_to": "posix"},
13
+ }
14
+ assert hf.ActionRule(rule=hf.Rule(**rule_args)) == hf.ActionRule(**rule_args)
15
+
16
+
17
+ def test_equivalent_init_json_like_with_rule_args() -> None:
18
+ rule_args: dict = {
19
+ "path": "resources.os_name",
20
+ "condition": {"value.equal_to": "posix"},
21
+ }
22
+ assert hf.ActionRule.from_json_like(
23
+ {"rule": rule_args}
24
+ ) == hf.ActionRule.from_json_like(rule_args)
@@ -0,0 +1,132 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ import sys
4
+ from typing import TYPE_CHECKING
5
+ import pytest
6
+ import requests
7
+
8
+ from hpcflow.app import app as hf
9
+
10
+ if TYPE_CHECKING:
11
+ from hpcflow.sdk.core.actions import Action, ActionEnvironment
12
+
13
+
14
+ @pytest.fixture
15
+ def act_env_1() -> ActionEnvironment:
16
+ return hf.ActionEnvironment(environment="env_1")
17
+
18
+
19
+ @pytest.fixture
20
+ def act_1(act_env_1) -> Action:
21
+ return hf.Action(
22
+ commands=[hf.Command("<<parameter:p1>>")],
23
+ environments=[act_env_1],
24
+ )
25
+
26
+
27
+ def test_shared_data_from_json_like_with_shared_data_dependency(act_1: Action):
28
+ """Check we can generate some shared data objects where one depends on another."""
29
+
30
+ p1 = hf.Parameter("p1")
31
+ p1._set_hash()
32
+ p1_hash = p1._hash_value
33
+ assert p1_hash is not None
34
+
35
+ ts1 = hf.TaskSchema(objective="ts1", actions=[act_1], inputs=[p1])
36
+ ts1._set_hash()
37
+ ts1_hash = ts1._hash_value
38
+ assert ts1_hash is not None
39
+
40
+ env_label = ts1.actions[0].environments[0].environment
41
+
42
+ shared_data_json: dict[str, dict] = {
43
+ "parameters": {
44
+ p1_hash: {
45
+ "is_file": p1.is_file,
46
+ "sub_parameters": [],
47
+ "type": p1.typ,
48
+ }
49
+ },
50
+ "task_schemas": {
51
+ ts1_hash: {
52
+ "method": ts1.method,
53
+ "implementation": ts1.implementation,
54
+ "version": ts1.version,
55
+ "objective": ts1.objective.name,
56
+ "inputs": [{"parameter": f"hash:{p1_hash}", "labels": {"": {}}}],
57
+ "outputs": [],
58
+ "actions": [
59
+ {
60
+ "_from_expand": False,
61
+ "script": None,
62
+ "commands": [
63
+ {
64
+ "command": "<<parameter:p1>>",
65
+ "executable": None,
66
+ "arguments": None,
67
+ "stdout": None,
68
+ "stderr": None,
69
+ "stdin": None,
70
+ }
71
+ ],
72
+ "input_files": [],
73
+ "output_files": [],
74
+ "input_file_generators": [],
75
+ "output_file_parsers": [],
76
+ "environments": [
77
+ {
78
+ "scope": {"kwargs": {}, "type": "ANY"},
79
+ "environment": env_label,
80
+ }
81
+ ],
82
+ "rules": [],
83
+ }
84
+ ],
85
+ }
86
+ },
87
+ }
88
+
89
+ sh = hf.template_components_from_json_like(shared_data_json)
90
+
91
+ assert sh["parameters"] == hf.ParametersList([p1]) and sh[
92
+ "task_schemas"
93
+ ] == hf.TaskSchemasList([ts1])
94
+
95
+
96
+ def test_get_demo_data_manifest(null_config) -> None:
97
+ hf.get_demo_data_files_manifest()
98
+
99
+
100
+ @pytest.mark.xfail(
101
+ condition=sys.platform == "darwin",
102
+ raises=requests.exceptions.HTTPError,
103
+ reason=(
104
+ "GHA MacOS runners use the same IP address, so we get rate limited when "
105
+ "retrieving demo data from GitHub."
106
+ ),
107
+ )
108
+ def test_get_demo_data_cache(null_config) -> None:
109
+ hf.clear_demo_data_cache_dir()
110
+ hf.cache_demo_data_file("text_file.txt")
111
+ with hf.demo_data_cache_dir.joinpath("text_file.txt").open("rt") as fh:
112
+ contents = fh.read()
113
+ assert contents == "\n".join(f"{i}" for i in range(1, 11)) + "\n"
114
+
115
+
116
+ def test_list_demo_workflows():
117
+ # sanity checks
118
+ lst = hf.list_demo_workflows()
119
+ assert isinstance(lst, tuple)
120
+ assert all(isinstance(i, str) and "." not in i for i in lst) # no extension included
121
+
122
+
123
+ def test_get_demo_workflows():
124
+ # sanity checks
125
+ lst = hf.list_demo_workflows()
126
+ demo_paths = hf._get_demo_workflows()
127
+ # keys should be those in the list:
128
+ assert sorted(list(lst)) == sorted(list(demo_paths.keys()))
129
+
130
+ # values should be distinct, absolute paths:
131
+ assert all(isinstance(i, Path) and i.is_absolute() for i in demo_paths.values())
132
+ assert len(set(demo_paths.values())) == len(demo_paths)
@@ -0,0 +1,46 @@
1
+ from pathlib import Path
2
+ from hpcflow.sdk.core.cache import ObjectCache
3
+ from hpcflow.sdk.core.test_utils import make_workflow
4
+
5
+
6
+ def test_object_cache_dependencies_simple(tmp_path: Path):
7
+ wk = make_workflow(
8
+ schemas_spec=[
9
+ ({"p1": None}, ("p2",), "t1"),
10
+ ({"p2": None}, ("p3",), "t2"),
11
+ ({"p3": None}, ("p4",), "t3"),
12
+ ({"p4": None}, ("p5",), "t4"),
13
+ ],
14
+ path=tmp_path,
15
+ local_inputs={0: ("p1",)},
16
+ overwrite=True,
17
+ )
18
+ obj_cache = ObjectCache.build(wk, dependencies=True)
19
+ assert obj_cache.run_dependencies == {0: set(), 1: {0}, 2: {1}, 3: {2}}
20
+ assert obj_cache.run_dependents == {0: {1}, 1: {2}, 2: {3}, 3: set()}
21
+ assert obj_cache.iter_run_dependencies == {0: set(), 1: {0}, 2: {1}, 3: {2}}
22
+ assert obj_cache.iter_iter_dependencies == {
23
+ 0: set(),
24
+ 1: {0},
25
+ 2: {1},
26
+ 3: {2},
27
+ }
28
+ assert obj_cache.elem_iter_dependencies == {
29
+ 0: set(),
30
+ 1: {0},
31
+ 2: {1},
32
+ 3: {2},
33
+ }
34
+ assert obj_cache.elem_elem_dependencies == {
35
+ 0: set(),
36
+ 1: {0},
37
+ 2: {1},
38
+ 3: {2},
39
+ }
40
+ assert obj_cache.elem_elem_dependents == {0: {1}, 1: {2}, 2: {3}, 3: set()}
41
+ assert obj_cache.elem_elem_dependents_rec == {
42
+ 0: {1, 2, 3},
43
+ 1: {2, 3},
44
+ 2: {3},
45
+ 3: set(),
46
+ }
@@ -0,0 +1,172 @@
1
+ from pathlib import Path
2
+ import pytest
3
+
4
+ from click.testing import CliRunner
5
+ import click.exceptions
6
+
7
+ from hpcflow import __version__
8
+ from hpcflow.app import app as hf
9
+ from hpcflow.sdk.cli import ErrorPropagatingClickContext
10
+ from hpcflow.sdk.cli_common import BoolOrString
11
+
12
+
13
+ def test_version() -> None:
14
+ runner = CliRunner()
15
+ result = runner.invoke(hf.cli, args="--version")
16
+ assert result.output.strip() == f"hpcFlow, version {__version__}"
17
+
18
+
19
+ def test_BoolOrString_convert():
20
+ param_type = BoolOrString(["a"])
21
+ assert param_type.convert(True, None, None) == True
22
+ assert param_type.convert(False, None, None) == False
23
+ assert param_type.convert("yes", None, None) == True
24
+ assert param_type.convert("no", None, None) == False
25
+ assert param_type.convert("on", None, None) == True
26
+ assert param_type.convert("off", None, None) == False
27
+ assert param_type.convert("a", None, None) == "a"
28
+ with pytest.raises(click.exceptions.BadParameter):
29
+ param_type.convert("b", None, None)
30
+
31
+
32
+ def test_error_propagated_with_custom_context_class():
33
+ class MyException(ValueError):
34
+ pass
35
+
36
+ class MyContextManager:
37
+
38
+ # set to True when MyException is raised within this context manager
39
+ raised = False
40
+
41
+ def __enter__(self):
42
+ return self
43
+
44
+ def __exit__(self, exc_type, exc_val, exc_tb):
45
+ if exc_type == MyException:
46
+ self.__class__.raised = True
47
+
48
+ @click.group()
49
+ @click.pass_context
50
+ def cli(ctx):
51
+ ctx.with_resource(MyContextManager())
52
+
53
+ cli.context_class = ErrorPropagatingClickContext # use custom click Context
54
+
55
+ @cli.command(
56
+ name="my-command"
57
+ ) # explicit, because Click 8.2.0+ removes suffixes like "command" for some reason
58
+ def my_command():
59
+ raise MyException()
60
+
61
+ runner = CliRunner()
62
+ runner.invoke(cli, args="my-command")
63
+
64
+ assert MyContextManager.raised
65
+
66
+
67
+ def test_error_not_propagated_without_custom_context_class():
68
+ class MyException(ValueError):
69
+ pass
70
+
71
+ class MyContextManager:
72
+
73
+ # set to True when MyException is raised within this context manager
74
+ raised = False
75
+
76
+ def __enter__(self):
77
+ return self
78
+
79
+ def __exit__(self, exc_type, exc_val, exc_tb):
80
+ if exc_type == MyException:
81
+ self.__class__.raised = True
82
+
83
+ @click.group()
84
+ @click.pass_context
85
+ def cli(ctx):
86
+ ctx.with_resource(MyContextManager())
87
+
88
+ @cli.command()
89
+ def my_command():
90
+ raise MyException()
91
+
92
+ runner = CliRunner()
93
+ runner.invoke(cli, args="my-command")
94
+
95
+ assert not MyContextManager.raised
96
+
97
+
98
+ def test_std_stream_file_created(tmp_path):
99
+ """Test exception is intercepted and printed to the specified --std-stream file."""
100
+ error_file = tmp_path / "std_stream.txt"
101
+ runner = CliRunner()
102
+ result = runner.invoke(
103
+ hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --raise'
104
+ )
105
+ assert error_file.is_file()
106
+ std_stream_contents = error_file.read_text()
107
+ assert "ValueError: internal noop raised!" in std_stream_contents
108
+ assert result.exit_code == 1
109
+ assert result.exc_info[0] == SystemExit
110
+
111
+
112
+ def test_std_stream_file_not_created(null_config, tmp_path):
113
+ """Test std stream file is not created when no ouput/errors/exceptions"""
114
+ error_file = tmp_path / "std_stream.txt"
115
+ runner = CliRunner()
116
+ result = runner.invoke(hf.cli, args=f'--std-stream "{str(error_file)}" internal noop')
117
+ assert not error_file.is_file()
118
+ assert result.exit_code == 0
119
+
120
+
121
+ def test_cli_exception():
122
+ """Test exception is passed to click"""
123
+ runner = CliRunner()
124
+ result = runner.invoke(hf.cli, args="internal noop --raise")
125
+ assert result.exit_code == 1
126
+ assert result.exc_info[0] == ValueError
127
+
128
+
129
+ def test_cli_click_exit_code_zero(tmp_path):
130
+ """Test Click's `Exit` exception is ignored by the `redirect_std_to_file` context manager when the exit code is zero."""
131
+ error_file = tmp_path / "std_stream.txt"
132
+ runner = CliRunner()
133
+ result = runner.invoke(
134
+ hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --click-exit-code 0'
135
+ )
136
+ assert result.exit_code == 0
137
+ assert not error_file.is_file()
138
+
139
+
140
+ def test_cli_click_exit_code_non_zero(tmp_path):
141
+ """Test Click's `Exit` exception is not ignored by the `redirect_std_to_file` context manager when the exit code is non-zero."""
142
+ error_file = tmp_path / "std_stream.txt"
143
+ runner = CliRunner()
144
+ result = runner.invoke(
145
+ hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --click-exit-code 2'
146
+ )
147
+ assert result.exit_code == 2
148
+ assert error_file.is_file()
149
+
150
+
151
+ def test_cli_make_demo_workflow(new_null_config, tmp_path):
152
+ """Check the demo workflow directory is generated."""
153
+ runner = CliRunner()
154
+ result = runner.invoke(
155
+ hf.cli, args=f'demo-workflow make workflow_1 --path "{str(tmp_path)}"'
156
+ )
157
+ assert result.exit_code == 0
158
+ assert Path(result.stdout_bytes.decode().strip()).is_dir()
159
+
160
+
161
+ def test_cli_make_demo_workflow_add_sub(new_null_config, tmp_path):
162
+ """Check the demo workflow directory is generated, and a submission is added."""
163
+ runner = CliRunner()
164
+ result = runner.invoke(
165
+ hf.cli,
166
+ args=f'demo-workflow make workflow_1 --path "{str(tmp_path)}" --add-submission',
167
+ )
168
+ assert result.exit_code == 0
169
+ wk_path = Path(result.stdout_bytes.decode().strip())
170
+ assert wk_path.is_dir()
171
+ wk = hf.Workflow(wk_path)
172
+ assert len(wk.submissions) == 1