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,827 @@
1
+ from __future__ import annotations
2
+ import copy
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from textwrap import dedent
6
+ from typing import TYPE_CHECKING
7
+
8
+ import pytest
9
+
10
+ from hpcflow.app import app as hf
11
+ from hpcflow.sdk.core.errors import (
12
+ MissingInputs,
13
+ WorkflowBatchUpdateFailedError,
14
+ WorkflowNotFoundError,
15
+ )
16
+ from hpcflow.sdk.core.test_utils import (
17
+ make_workflow,
18
+ P1_parameter_cls as P1,
19
+ make_test_data_YAML_workflow,
20
+ )
21
+
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Iterator
24
+ from hpcflow.sdk.core.actions import Action, ActionEnvironment
25
+ from hpcflow.sdk.core.command_files import FileSpec
26
+ from hpcflow.sdk.core.parameters import Parameter
27
+ from hpcflow.sdk.core.task_schema import TaskSchema
28
+ from hpcflow.sdk.core.workflow import Workflow
29
+
30
+
31
+ @pytest.fixture
32
+ def persistent_workflow(null_config) -> Iterator[Workflow]:
33
+ tmp_dir = hf._ensure_user_runtime_dir().joinpath("test_data")
34
+ tmp_dir.mkdir(exist_ok=True)
35
+ wk = make_test_data_YAML_workflow("workflow_1.yaml", path=tmp_dir, overwrite=True)
36
+ yield wk
37
+ wk._delete_no_confirm()
38
+
39
+
40
+ @pytest.mark.skip(reason="Cannot delete the zip file! Something still has a handle.")
41
+ def test_workflow_zip(persistent_workflow):
42
+ zip_path = persistent_workflow.zip()
43
+
44
+ # check zipped is loadable:
45
+ wk_zipped = hf.Workflow(zip_path)
46
+ assert wk_zipped.tasks[0].elements[0].get("inputs.p1") == 101
47
+
48
+ del wk_zipped._store.fs
49
+ del wk_zipped._store
50
+ del wk_zipped
51
+
52
+ # delete the zipped workflow:
53
+ Path(zip_path).unlink()
54
+
55
+
56
+ def modify_workflow_metadata_on_disk(workflow: Workflow):
57
+ """Make a non-sense change to the on-disk metadata."""
58
+ assert workflow.store_format == "zarr"
59
+ wk_md = workflow._store.load_metadata() # type: ignore
60
+ changed_md = copy.deepcopy(wk_md)
61
+ changed_md["new_key"] = "new_value"
62
+ workflow._store._get_root_group(mode="r+").attrs.put(changed_md) # type: ignore
63
+
64
+
65
+ def make_workflow_w1_with_config_kwargs(
66
+ config_kwargs, path, param_p1: Parameter, param_p2: Parameter
67
+ ) -> Workflow:
68
+ hf.load_config(**config_kwargs)
69
+ s1 = hf.TaskSchema("ts1", actions=[], inputs=[param_p1], outputs=[param_p2])
70
+ t1 = hf.Task(schema=s1, inputs=[hf.InputValue(param_p1, 101)])
71
+ wkt = hf.WorkflowTemplate(name="w1", tasks=[t1])
72
+ return hf.Workflow.from_template(wkt, path=path)
73
+
74
+
75
+ @pytest.fixture
76
+ def null_config(tmp_path):
77
+ if not hf.is_config_loaded:
78
+ hf.load_config(config_dir=tmp_path)
79
+
80
+
81
+ @pytest.fixture
82
+ def empty_workflow(null_config, tmp_path) -> Workflow:
83
+ return hf.Workflow.from_template(hf.WorkflowTemplate(name="w1"), path=tmp_path)
84
+
85
+
86
+ @pytest.fixture
87
+ def param_p1(null_config) -> Parameter:
88
+ return hf.Parameter("p1")
89
+
90
+
91
+ @pytest.fixture
92
+ def param_p1c(null_config) -> Parameter:
93
+ return hf.Parameter("p1c")
94
+
95
+
96
+ @pytest.fixture
97
+ def param_p2() -> Parameter:
98
+ return hf.Parameter("p2")
99
+
100
+
101
+ @pytest.fixture
102
+ def param_p3(null_config) -> Parameter:
103
+ return hf.Parameter("p3")
104
+
105
+
106
+ @pytest.fixture
107
+ def act_env_1(null_config) -> ActionEnvironment:
108
+ return hf.ActionEnvironment("env_1")
109
+
110
+
111
+ @pytest.fixture
112
+ def act_1(null_config, act_env_1: ActionEnvironment) -> Action:
113
+ return hf.Action(
114
+ commands=[hf.Command("<<parameter:p1>>")],
115
+ environments=[act_env_1],
116
+ )
117
+
118
+
119
+ @pytest.fixture
120
+ def act_2(null_config, act_env_1: ActionEnvironment) -> Action:
121
+ return hf.Action(
122
+ commands=[hf.Command("<<parameter:p2>> <<parameter:p3>>")],
123
+ environments=[act_env_1],
124
+ )
125
+
126
+
127
+ @pytest.fixture
128
+ def file_spec_fs1(
129
+ null_config,
130
+ ) -> FileSpec:
131
+ return hf.FileSpec(label="file1", name="file1.txt")
132
+
133
+
134
+ @pytest.fixture
135
+ def act_3(
136
+ null_config,
137
+ act_env_1: ActionEnvironment,
138
+ param_p2: Parameter,
139
+ file_spec_fs1: FileSpec,
140
+ ) -> Action:
141
+ return hf.Action(
142
+ commands=[hf.Command("<<parameter:p1>>")],
143
+ output_file_parsers=[
144
+ hf.OutputFileParser(output=param_p2, output_files=[file_spec_fs1]),
145
+ ],
146
+ environments=[act_env_1],
147
+ )
148
+
149
+
150
+ @pytest.fixture
151
+ def schema_s1(null_config, param_p1: Parameter, act_1: Action) -> TaskSchema:
152
+ return hf.TaskSchema("ts1", actions=[act_1], inputs=[param_p1])
153
+
154
+
155
+ @pytest.fixture
156
+ def schema_s2(
157
+ null_config, param_p2: Parameter, param_p3: Parameter, act_2: Action
158
+ ) -> TaskSchema:
159
+ return hf.TaskSchema("ts2", actions=[act_2], inputs=[param_p2, param_p3])
160
+
161
+
162
+ @pytest.fixture
163
+ def schema_s3(
164
+ null_config, param_p1: Parameter, param_p2: Parameter, act_3: Action
165
+ ) -> TaskSchema:
166
+ return hf.TaskSchema("ts1", actions=[act_3], inputs=[param_p1], outputs=[param_p2])
167
+
168
+
169
+ @pytest.fixture
170
+ def schema_s4(
171
+ null_config,
172
+ param_p1: Parameter,
173
+ ) -> TaskSchema:
174
+ return hf.TaskSchema(
175
+ objective="t1",
176
+ inputs=[hf.SchemaInput(parameter=param_p1)],
177
+ actions=[
178
+ hf.Action(
179
+ environments=[hf.ActionEnvironment("null_env")],
180
+ commands=[hf.Command("Write-Output '<<parameter:p1>>'")],
181
+ )
182
+ ],
183
+ )
184
+
185
+
186
+ @pytest.fixture
187
+ def schema_s4c(
188
+ null_config,
189
+ param_p1c: Parameter,
190
+ ) -> TaskSchema:
191
+ return hf.TaskSchema(
192
+ objective="t1",
193
+ inputs=[hf.SchemaInput(parameter=param_p1c)],
194
+ actions=[
195
+ hf.Action(
196
+ environments=[hf.ActionEnvironment("null_env")],
197
+ commands=[hf.Command("Write-Output '<<parameter:p1c>>'")],
198
+ )
199
+ ],
200
+ )
201
+
202
+
203
+ @pytest.fixture
204
+ def workflow_w1(
205
+ null_config, tmp_path: Path, schema_s3: TaskSchema, param_p1: Parameter
206
+ ) -> Workflow:
207
+ t1 = hf.Task(schema=schema_s3, inputs=[hf.InputValue(param_p1, 101)])
208
+ wkt = hf.WorkflowTemplate(name="w1", tasks=[t1])
209
+ return hf.Workflow.from_template(wkt, path=tmp_path)
210
+
211
+
212
+ def test_make_empty_workflow(null_config, empty_workflow: Workflow):
213
+ assert empty_workflow.path is not None
214
+
215
+
216
+ def test_raise_on_missing_workflow(null_config, tmp_path: Path):
217
+ with pytest.raises(WorkflowNotFoundError):
218
+ hf.Workflow(tmp_path)
219
+
220
+
221
+ def test_add_empty_task(empty_workflow: Workflow, schema_s1: TaskSchema):
222
+ t1 = hf.Task(schema=schema_s1)
223
+ wk_t1 = empty_workflow._add_empty_task(t1)
224
+ assert len(empty_workflow.tasks) == 1 and wk_t1.index == 0 and wk_t1.name == "ts1"
225
+
226
+
227
+ def test_raise_on_missing_inputs_add_first_task(
228
+ empty_workflow: Workflow, schema_s1: TaskSchema, param_p1: Parameter
229
+ ):
230
+ t1 = hf.Task(schema=schema_s1)
231
+ with pytest.raises(MissingInputs) as exc_info:
232
+ empty_workflow.add_task(t1)
233
+
234
+ assert exc_info.value.missing_inputs == (param_p1.typ,)
235
+
236
+
237
+ def test_raise_on_missing_inputs_add_second_task(
238
+ workflow_w1: Workflow, schema_s2: TaskSchema, param_p3: Parameter
239
+ ):
240
+ t2 = hf.Task(schema=schema_s2)
241
+ with pytest.raises(MissingInputs) as exc_info:
242
+ workflow_w1.add_task(t2)
243
+
244
+ assert exc_info.value.missing_inputs == (param_p3.typ,) # p2 comes from existing task
245
+
246
+
247
+ @pytest.mark.skip(reason="TODO: Not implemented.")
248
+ def test_new_workflow_deleted_on_creation_failure():
249
+ pass
250
+
251
+
252
+ def test_WorkflowTemplate_from_YAML_string(null_config):
253
+ wkt_yml = dedent(
254
+ """
255
+ name: simple_workflow
256
+
257
+ tasks:
258
+ - schema: dummy_task_1
259
+ element_sets:
260
+ - inputs:
261
+ p2: 201
262
+ p5: 501
263
+ sequences:
264
+ - path: inputs.p1
265
+ nesting_order: 0
266
+ values: [101, 102]
267
+ """
268
+ )
269
+ hf.WorkflowTemplate.from_YAML_string(wkt_yml)
270
+
271
+
272
+ def test_WorkflowTemplate_from_YAML_string_without_element_sets(null_config):
273
+ wkt_yml = dedent(
274
+ """
275
+ name: simple_workflow
276
+
277
+ tasks:
278
+ - schema: dummy_task_1
279
+ inputs:
280
+ p2: 201
281
+ p5: 501
282
+ sequences:
283
+ - path: inputs.p1
284
+ nesting_order: 0
285
+ values: [101, 102]
286
+ """
287
+ )
288
+ hf.WorkflowTemplate.from_YAML_string(wkt_yml)
289
+
290
+
291
+ def test_WorkflowTemplate_from_YAML_string_with_and_without_element_sets_equivalence(
292
+ null_config,
293
+ ):
294
+ wkt_yml_1 = dedent(
295
+ """
296
+ name: simple_workflow
297
+
298
+ tasks:
299
+ - schema: dummy_task_1
300
+ element_sets:
301
+ - inputs:
302
+ p2: 201
303
+ p5: 501
304
+ sequences:
305
+ - path: inputs.p1
306
+ nesting_order: 0
307
+ values: [101, 102]
308
+ """
309
+ )
310
+ wkt_yml_2 = dedent(
311
+ """
312
+ name: simple_workflow
313
+
314
+ tasks:
315
+ - schema: dummy_task_1
316
+ inputs:
317
+ p2: 201
318
+ p5: 501
319
+ sequences:
320
+ - path: inputs.p1
321
+ nesting_order: 0
322
+ values: [101, 102]
323
+ """
324
+ )
325
+ wkt_1 = hf.WorkflowTemplate.from_YAML_string(wkt_yml_1)
326
+ wkt_2 = hf.WorkflowTemplate.from_YAML_string(wkt_yml_2)
327
+ assert wkt_1 == wkt_2
328
+
329
+
330
+ def test_store_has_pending_during_add_task(
331
+ workflow_w1: Workflow, schema_s2: TaskSchema, param_p3: Parameter
332
+ ):
333
+ t2 = hf.Task(schema=schema_s2, inputs=[hf.InputValue(param_p3, 301)])
334
+ with workflow_w1.batch_update():
335
+ workflow_w1.add_task(t2)
336
+ assert workflow_w1._store.has_pending
337
+
338
+
339
+ def test_empty_batch_update_does_nothing(workflow_w1: Workflow):
340
+ with workflow_w1.batch_update():
341
+ assert not workflow_w1._store.has_pending
342
+
343
+
344
+ @pytest.mark.skip("need to re-implement `is_modified_on_disk`")
345
+ def test_is_modified_on_disk_when_metadata_changed(workflow_w1: Workflow):
346
+ # this is ZarrPersistentStore-specific; might want to consider a refactor later
347
+ with workflow_w1._store.cached_load():
348
+ modify_workflow_metadata_on_disk(workflow_w1)
349
+ assert workflow_w1._store.is_modified_on_disk() # type: ignore
350
+
351
+
352
+ @pytest.mark.skip("need to re-implement `is_modified_on_disk`")
353
+ def test_batch_update_abort_if_modified_on_disk(
354
+ workflow_w1: Workflow, schema_s2: TaskSchema, param_p3: Parameter
355
+ ):
356
+ t2 = hf.Task(schema=schema_s2, inputs=[hf.InputValue(param_p3, 301)])
357
+ with pytest.raises(WorkflowBatchUpdateFailedError):
358
+ with workflow_w1._store.cached_load(), workflow_w1.batch_update():
359
+ workflow_w1.add_task(t2)
360
+ modify_workflow_metadata_on_disk(workflow_w1)
361
+
362
+
363
+ def test_closest_task_input_source_chosen(null_config, tmp_path: Path):
364
+ wk = make_workflow(
365
+ schemas_spec=[
366
+ ({"p1": None}, ("p1",), "t1"),
367
+ ({"p1": None}, ("p1",), "t2"),
368
+ ({"p1": None}, ("p1",), "t3"),
369
+ ],
370
+ local_inputs={0: ("p1",)},
371
+ path=tmp_path,
372
+ )
373
+ assert wk.tasks.t3.get_task_dependencies(as_objects=True) == [wk.tasks.t2]
374
+
375
+
376
+ def test_WorkflowTemplate_from_JSON_string_without_element_sets(null_config):
377
+ wkt_json = dedent(
378
+ """
379
+ {
380
+ "name": "test_wk",
381
+ "tasks": [
382
+ {
383
+ "schema": "test_t1_bash",
384
+ "inputs": {
385
+ "p1": 101
386
+ }
387
+ }
388
+ ]
389
+ }
390
+ """
391
+ )
392
+ hf.WorkflowTemplate.from_JSON_string(wkt_json)
393
+
394
+
395
+ @pytest.mark.parametrize("store", ["json", "zarr"])
396
+ def test_equivalent_element_input_parameter_value_class_and_kwargs(
397
+ null_config,
398
+ tmp_path: Path,
399
+ store: str,
400
+ schema_s4c: TaskSchema,
401
+ param_p1c: Parameter,
402
+ ):
403
+ a_value = 101
404
+ t1_1 = hf.Task(
405
+ schema=[schema_s4c],
406
+ inputs=[hf.InputValue(parameter=param_p1c, value=P1(a=a_value))],
407
+ )
408
+ t1_2 = hf.Task(
409
+ schema=[schema_s4c],
410
+ inputs=[hf.InputValue(parameter=param_p1c, value={"a": a_value})],
411
+ )
412
+ wk = hf.Workflow.from_template_data(
413
+ tasks=[t1_1, t1_2],
414
+ path=tmp_path,
415
+ template_name="temp",
416
+ store=store,
417
+ )
418
+ assert (
419
+ wk.tasks.t1_1.elements[0].inputs.p1c.value
420
+ == wk.tasks.t1_2.elements[0].inputs.p1c.value
421
+ )
422
+
423
+
424
+ @pytest.mark.parametrize("store", ["json", "zarr"])
425
+ def test_equivalent_element_input_parameter_value_class_method_and_kwargs(
426
+ null_config,
427
+ tmp_path: Path,
428
+ store: str,
429
+ schema_s4c: TaskSchema,
430
+ param_p1c: Parameter,
431
+ ):
432
+ b_val = 50
433
+ c_val = 51
434
+ expected_a_val = b_val + c_val
435
+ t1_1 = hf.Task(
436
+ schema=[schema_s4c],
437
+ inputs=[hf.InputValue(parameter=param_p1c, value=P1.from_data(b=b_val, c=c_val))],
438
+ )
439
+ t1_2 = hf.Task(
440
+ schema=[schema_s4c],
441
+ inputs=[
442
+ hf.InputValue(
443
+ parameter=param_p1c,
444
+ value={"b": b_val, "c": c_val},
445
+ value_class_method="from_data",
446
+ )
447
+ ],
448
+ )
449
+ wk = hf.Workflow.from_template_data(
450
+ tasks=[t1_1, t1_2],
451
+ path=tmp_path,
452
+ template_name="temp",
453
+ store=store,
454
+ )
455
+ assert wk.tasks.t1_1.elements[0].inputs.p1c.value.a == expected_a_val
456
+ assert (
457
+ wk.tasks.t1_1.elements[0].inputs.p1c.value
458
+ == wk.tasks.t1_2.elements[0].inputs.p1c.value
459
+ )
460
+
461
+
462
+ @pytest.mark.parametrize("store", ["json", "zarr"])
463
+ def test_input_value_class_expected_value(
464
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema, param_p1c: Parameter
465
+ ):
466
+ a_value = 101
467
+ t1_value_exp = P1(a=a_value)
468
+ t2_value_exp = {"a": a_value}
469
+ t1_1 = hf.Task(
470
+ schema=[schema_s4c],
471
+ inputs=[hf.InputValue(parameter=param_p1c, value=t1_value_exp)],
472
+ )
473
+ t1_2 = hf.Task(
474
+ schema=[schema_s4c],
475
+ inputs=[hf.InputValue(parameter=param_p1c, value=t2_value_exp)],
476
+ )
477
+ wk = hf.Workflow.from_template_data(
478
+ tasks=[t1_1, t1_2],
479
+ path=tmp_path,
480
+ template_name="temp",
481
+ store=store,
482
+ )
483
+ value_1 = wk.tasks.t1_1.template.element_sets[0].inputs[0].value
484
+ value_2 = wk.tasks.t1_2.template.element_sets[0].inputs[0].value
485
+ assert value_1 == t1_value_exp
486
+ assert value_2 == t2_value_exp
487
+
488
+
489
+ @pytest.mark.parametrize("store", ["json", "zarr"])
490
+ def test_input_value_class_method_expected_value(
491
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema, param_p1c: Parameter
492
+ ):
493
+ b_val = 50
494
+ c_val = 51
495
+ t1_value_exp = P1.from_data(b=b_val, c=c_val)
496
+ t2_value_exp = {"b": b_val, "c": c_val}
497
+ t1_1 = hf.Task(
498
+ schema=[schema_s4c],
499
+ inputs=[hf.InputValue(parameter=param_p1c, value=t1_value_exp)],
500
+ )
501
+ t1_2 = hf.Task(
502
+ schema=[schema_s4c],
503
+ inputs=[
504
+ hf.InputValue(
505
+ parameter=param_p1c,
506
+ value=t2_value_exp,
507
+ value_class_method="from_data",
508
+ )
509
+ ],
510
+ )
511
+ wk = hf.Workflow.from_template_data(
512
+ tasks=[t1_1, t1_2],
513
+ path=tmp_path,
514
+ template_name="temp",
515
+ store=store,
516
+ )
517
+ value_1 = wk.tasks.t1_1.template.element_sets[0].inputs[0].value
518
+ value_2 = wk.tasks.t1_2.template.element_sets[0].inputs[0].value
519
+ assert value_1 == t1_value_exp
520
+ assert value_2 == t2_value_exp
521
+
522
+
523
+ @pytest.mark.parametrize("store", ["json", "zarr"])
524
+ def test_equivalent_element_input_sequence_parameter_value_class_and_kwargs(
525
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema
526
+ ):
527
+ data = {"a": 101}
528
+ obj = P1(**data) # type: ignore[arg-type] # python/mypy#15317
529
+ t1_1 = hf.Task(
530
+ schema=[schema_s4c],
531
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[obj], nesting_order=0)],
532
+ )
533
+ t1_2 = hf.Task(
534
+ schema=[schema_s4c],
535
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[data], nesting_order=0)],
536
+ )
537
+ wk = hf.Workflow.from_template_data(
538
+ tasks=[t1_1, t1_2],
539
+ path=tmp_path,
540
+ template_name="temp",
541
+ store=store,
542
+ )
543
+ val_1 = wk.tasks.t1_1.elements[0].inputs.p1c.value
544
+ val_2 = wk.tasks.t1_2.elements[0].inputs.p1c.value
545
+ assert val_1 == val_2 == obj
546
+
547
+
548
+ @pytest.mark.parametrize("store", ["json", "zarr"])
549
+ def test_equivalent_element_input_sequence_parameter_value_class_method_and_kwargs(
550
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema
551
+ ):
552
+ data = {"b": 50, "c": 51}
553
+ obj = P1.from_data(**data)
554
+ t1_1 = hf.Task(
555
+ schema=[schema_s4c],
556
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[obj], nesting_order=0)],
557
+ )
558
+ t1_2 = hf.Task(
559
+ schema=[schema_s4c],
560
+ sequences=[
561
+ hf.ValueSequence(
562
+ path="inputs.p1c",
563
+ values=[data],
564
+ value_class_method="from_data",
565
+ nesting_order=0,
566
+ )
567
+ ],
568
+ )
569
+ wk = hf.Workflow.from_template_data(
570
+ tasks=[t1_1, t1_2],
571
+ path=tmp_path,
572
+ template_name="temp",
573
+ store=store,
574
+ )
575
+ val_1 = wk.tasks.t1_1.elements[0].inputs.p1c.value
576
+ val_2 = wk.tasks.t1_2.elements[0].inputs.p1c.value
577
+ assert val_1 == val_2 == obj
578
+
579
+
580
+ @pytest.mark.parametrize("store", ["json", "zarr"])
581
+ def test_sequence_value_class_expected_value(
582
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema
583
+ ):
584
+ data = {"a": 101}
585
+ obj = P1(**data) # type: ignore[arg-type] # python/mypy#15317
586
+ t1_1 = hf.Task(
587
+ schema=[schema_s4c],
588
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[obj], nesting_order=0)],
589
+ )
590
+ t1_2 = hf.Task(
591
+ schema=[schema_s4c],
592
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[data], nesting_order=0)],
593
+ )
594
+ wk = hf.Workflow.from_template_data(
595
+ tasks=[t1_1, t1_2],
596
+ path=tmp_path,
597
+ template_name="temp",
598
+ store=store,
599
+ )
600
+ value_1 = wk.tasks.t1_1.template.element_sets[0].sequences[0].values[0]
601
+ value_2 = wk.tasks.t1_2.template.element_sets[0].sequences[0].values[0]
602
+ assert value_1 == obj
603
+ assert value_2 == data
604
+
605
+
606
+ @pytest.mark.parametrize("store", ["json", "zarr"])
607
+ def test_sequence_value_class_method_expected_value(
608
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema
609
+ ):
610
+ data = {"b": 50, "c": 51}
611
+ obj = P1.from_data(**data)
612
+ t1_1 = hf.Task(
613
+ schema=[schema_s4c],
614
+ sequences=[hf.ValueSequence(path="inputs.p1c", values=[obj], nesting_order=0)],
615
+ )
616
+ t1_2 = hf.Task(
617
+ schema=[schema_s4c],
618
+ sequences=[
619
+ hf.ValueSequence(
620
+ path="inputs.p1c",
621
+ values=[data],
622
+ nesting_order=0,
623
+ value_class_method="from_data",
624
+ ),
625
+ ],
626
+ )
627
+ wk = hf.Workflow.from_template_data(
628
+ tasks=[t1_1, t1_2],
629
+ path=tmp_path,
630
+ template_name="temp",
631
+ store=store,
632
+ )
633
+ value_1 = wk.tasks.t1_1.template.element_sets[0].sequences[0].values[0]
634
+ value_2 = wk.tasks.t1_2.template.element_sets[0].sequences[0].values[0]
635
+ assert value_1 == obj
636
+ assert value_2 == data
637
+
638
+
639
+ @pytest.mark.parametrize("store", ["json", "zarr"])
640
+ def test_expected_element_input_parameter_value_class_merge_sequence(
641
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema, param_p1c: Parameter
642
+ ):
643
+ a_val = 101
644
+ d_val = 201
645
+ obj_exp = P1(a=a_val, d=d_val)
646
+
647
+ t1 = hf.Task(
648
+ schema=[schema_s4c],
649
+ inputs=[hf.InputValue(parameter=param_p1c, value={"a": a_val})],
650
+ sequences=[
651
+ hf.ValueSequence(path="inputs.p1c.d", values=[d_val], nesting_order=0)
652
+ ],
653
+ )
654
+ wk = hf.Workflow.from_template_data(
655
+ tasks=[t1],
656
+ path=tmp_path,
657
+ template_name="temp",
658
+ store=store,
659
+ )
660
+ wk.tasks.t1.template.element_sets[0].sequences[0].values[0]
661
+ assert wk.tasks.t1.elements[0].inputs.p1c.value == obj_exp
662
+
663
+
664
+ @pytest.mark.parametrize("store", ["json", "zarr"])
665
+ def test_expected_element_input_parameter_value_class_method_merge_sequence(
666
+ null_config, tmp_path: Path, store: str, schema_s4c: TaskSchema, param_p1c: Parameter
667
+ ):
668
+ b_val = 50
669
+ c_val = 51
670
+ obj_exp = P1.from_data(b=b_val, c=c_val)
671
+
672
+ t1 = hf.Task(
673
+ schema=[schema_s4c],
674
+ inputs=[
675
+ hf.InputValue(
676
+ parameter=param_p1c, value={"b": b_val}, value_class_method="from_data"
677
+ )
678
+ ],
679
+ sequences=[
680
+ hf.ValueSequence(path="inputs.p1c.c", values=[c_val], nesting_order=0)
681
+ ],
682
+ )
683
+ wk = hf.Workflow.from_template_data(
684
+ tasks=[t1],
685
+ path=tmp_path,
686
+ template_name="temp",
687
+ store=store,
688
+ )
689
+ wk.tasks.t1.template.element_sets[0].sequences[0].values[0]
690
+ assert wk.tasks.t1.elements[0].inputs.p1c.value == obj_exp
691
+
692
+
693
+ @pytest.mark.parametrize("store", ["json", "zarr"])
694
+ def test_upstream_input_source_merge_with_current_input_modification(
695
+ null_config, tmp_path: Path, store: str, param_p2: Parameter
696
+ ):
697
+ s1 = hf.TaskSchema(objective="t1", inputs=[hf.SchemaInput(parameter=param_p2)])
698
+ s2 = hf.TaskSchema(objective="t2", inputs=[hf.SchemaInput(parameter=param_p2)])
699
+ tasks = [
700
+ hf.Task(schema=s1, inputs=[hf.InputValue("p2", {"a": 101})]),
701
+ hf.Task(schema=s2, inputs=[hf.InputValue("p2", value=102, path="b")]),
702
+ ]
703
+ wk = hf.Workflow.from_template_data(
704
+ tasks=tasks,
705
+ path=tmp_path,
706
+ template_name="temp",
707
+ store=store,
708
+ )
709
+ p2 = wk.tasks[1].elements[0].inputs.p2
710
+ assert isinstance(p2, hf.ElementParameter)
711
+ assert p2.value == {"a": 101, "b": 102}
712
+
713
+
714
+ @pytest.mark.parametrize("store", ["json", "zarr"])
715
+ def test_upstream_input_source_with_sub_parameter(
716
+ null_config, tmp_path: Path, store: str, param_p2: Parameter
717
+ ):
718
+ s1 = hf.TaskSchema(objective="t1", inputs=[hf.SchemaInput(parameter=param_p2)])
719
+ s2 = hf.TaskSchema(objective="t2", inputs=[hf.SchemaInput(parameter=param_p2)])
720
+ tasks = [
721
+ hf.Task(
722
+ schema=s1,
723
+ inputs=[
724
+ hf.InputValue("p2", {"a": 101}),
725
+ hf.InputValue("p2", value=102, path="b"),
726
+ ],
727
+ ),
728
+ hf.Task(schema=s2),
729
+ ]
730
+ wk = hf.Workflow.from_template_data(
731
+ tasks=tasks,
732
+ path=tmp_path,
733
+ template_name="temp",
734
+ store=store,
735
+ )
736
+ p2 = wk.tasks[1].elements[0].inputs.p2
737
+ assert isinstance(p2, hf.ElementParameter)
738
+ assert p2.value == {"a": 101, "b": 102}
739
+
740
+
741
+ @pytest.mark.parametrize("store", ["json", "zarr"])
742
+ def test_from_template_data_workflow_reload(null_config, tmp_path: Path, store: str):
743
+ wk_name = "temp"
744
+ t1 = hf.Task(schema=hf.task_schemas.test_t1_ps, inputs=[hf.InputValue("p1", 101)])
745
+ wk = hf.Workflow.from_template_data(
746
+ tasks=[t1],
747
+ path=tmp_path,
748
+ template_name="temp",
749
+ store=store,
750
+ )
751
+ wk_ld = hf.Workflow(wk.path)
752
+ assert (
753
+ wk.tasks[0].elements[0].get_data_idx()
754
+ == wk_ld.tasks[0].elements[0].get_data_idx()
755
+ )
756
+
757
+
758
+ @pytest.mark.parametrize("store", ["json", "zarr"])
759
+ def test_from_template_workflow_reload(null_config, tmp_path: Path, store: str):
760
+ wk_name = "temp"
761
+ t1 = hf.Task(schema=hf.task_schemas.test_t1_ps, inputs=[hf.InputValue("p1", 101)])
762
+ wkt = hf.WorkflowTemplate(name=wk_name, tasks=[t1])
763
+ wk = hf.Workflow.from_template(
764
+ template=wkt,
765
+ path=tmp_path,
766
+ store=store,
767
+ )
768
+ wk_ld = hf.Workflow(wk.path)
769
+ assert (
770
+ wk.tasks[0].elements[0].get_data_idx()
771
+ == wk_ld.tasks[0].elements[0].get_data_idx()
772
+ )
773
+
774
+
775
+ @pytest.mark.parametrize("store", ["json", "zarr"])
776
+ def test_from_YAML_str_template_workflow_reload(null_config, tmp_path: Path, store: str):
777
+ yaml_str = dedent(
778
+ """
779
+ name: temp
780
+ tasks:
781
+ - schema: test_t1_ps
782
+ inputs:
783
+ p1: 101
784
+ """
785
+ )
786
+ wk = hf.Workflow.from_YAML_string(
787
+ YAML_str=yaml_str,
788
+ path=tmp_path,
789
+ store=store,
790
+ )
791
+ wk_ld = hf.Workflow(wk.path)
792
+ assert (
793
+ wk.tasks[0].elements[0].get_data_idx()
794
+ == wk_ld.tasks[0].elements[0].get_data_idx()
795
+ )
796
+
797
+
798
+ @pytest.mark.parametrize("store", ["json", "zarr"])
799
+ def test_from_template_workflow_add_task_reload(null_config, tmp_path: Path, store: str):
800
+ wk_name = "temp"
801
+ t1 = hf.Task(schema=hf.task_schemas.test_t1_ps, inputs=[hf.InputValue("p1", 101)])
802
+ wkt = hf.WorkflowTemplate(name=wk_name)
803
+ wk = hf.Workflow.from_template(
804
+ template=wkt,
805
+ path=tmp_path,
806
+ store=store,
807
+ )
808
+ wk.add_task(t1)
809
+ wk_ld = hf.Workflow(wk.path)
810
+ assert (
811
+ wk.tasks[0].elements[0].get_data_idx()
812
+ == wk_ld.tasks[0].elements[0].get_data_idx()
813
+ )
814
+
815
+
816
+ @pytest.mark.parametrize("store", ["json", "zarr"])
817
+ def test_batch_update_mode_false_after_empty_workflow_init(
818
+ null_config, tmp_path: Path, store: str
819
+ ):
820
+ wk_name = "temp"
821
+ wk = hf.Workflow.from_template_data(
822
+ tasks=[],
823
+ path=tmp_path,
824
+ template_name=wk_name,
825
+ store=store,
826
+ )
827
+ assert wk._in_batch_mode == False