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,352 @@
1
+ """
2
+ Shell models based on Microsoft PowerShell.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ import subprocess
7
+ from textwrap import dedent
8
+ from typing import TYPE_CHECKING
9
+ from typing_extensions import override
10
+ from hpcflow.sdk.typing import hydrate
11
+ from hpcflow.sdk.submission.shells.base import Shell
12
+ from hpcflow.sdk.submission.shells.os_version import get_OS_info_windows
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import ClassVar
16
+ from .base import VersionInfo
17
+
18
+
19
+ @hydrate
20
+ class WindowsPowerShell(Shell):
21
+ """Class to represent using PowerShell on Windows to generate and submit a jobscript."""
22
+
23
+ # TODO: add snippets that can be used in demo task schemas?
24
+
25
+ #: Default for executable name.
26
+ DEFAULT_EXE: ClassVar[str] = "powershell.exe"
27
+
28
+ #: File extension for jobscripts.
29
+ JS_EXT: ClassVar[str] = ".ps1"
30
+ #: Basic indent.
31
+ JS_INDENT: ClassVar[str] = " "
32
+ #: Indent for environment setup.
33
+ JS_ENV_SETUP_INDENT: ClassVar[str] = 2 * JS_INDENT
34
+ #: Template for the jobscript shebang line.
35
+ JS_SHEBANG: ClassVar[str] = ""
36
+ #: Template for the jobscript functions file.
37
+ JS_FUNCS: ClassVar[str] = dedent(
38
+ """\
39
+ function {workflow_app_alias} {{
40
+ & {{
41
+ {env_setup}{app_invoc} `
42
+ --with-config log_file_path "$env:{app_caps}_LOG_PATH" `
43
+ --config-dir "{config_dir}" `
44
+ --config-key "{config_invoc_key}" `
45
+ $args
46
+ }} @args
47
+ }}
48
+
49
+ function get_nth_line($file, $line) {{
50
+ Get-Content $file | Select-Object -Skip $line -First 1
51
+ }}
52
+ """
53
+ )
54
+ #: Template for the common part of the jobscript header.
55
+ JS_HEADER: ClassVar[str] = dedent(
56
+ """\
57
+ $ErrorActionPreference = 'Stop'
58
+
59
+ function JoinMultiPath {{
60
+ $numArgs = $args.Length
61
+ $path = $args[0]
62
+ for ($i = 1; $i -lt $numArgs; $i++) {{
63
+ $path = Join-Path $path $args[$i]
64
+ }}
65
+ return $path
66
+ }}
67
+
68
+ $WK_PATH = $(Get-Location)
69
+ $WK_PATH_ARG = $WK_PATH
70
+ $SUB_IDX = {sub_idx}
71
+ $JS_IDX = {js_idx}
72
+
73
+ $SUB_DIR = JoinMultiPath $WK_PATH artifacts submissions $SUB_IDX
74
+ $JS_FUNCS_PATH = JoinMultiPath $SUB_DIR {jobscript_functions_dir} {jobscript_functions_name}
75
+ . $JS_FUNCS_PATH
76
+
77
+ $EAR_ID_FILE = JoinMultiPath $SUB_DIR {run_IDs_file_dir} {run_IDs_file_name}
78
+ $SUB_TMP_DIR = Join-Path $SUB_DIR {tmp_dir_name}
79
+ $SUB_LOG_DIR = Join-Path $SUB_DIR {log_dir_name}
80
+ $SUB_STD_DIR = Join-Path $SUB_DIR {app_std_dir_name}
81
+ $SUB_SCRIPTS_DIR = Join-Path $SUB_DIR {scripts_dir_name}
82
+
83
+ $env:{app_caps}_WK_PATH = $WK_PATH
84
+ $env:{app_caps}_WK_PATH_ARG = $WK_PATH_ARG
85
+ $env:{app_caps}_SUB_IDX = {sub_idx}
86
+ $env:{app_caps}_SUB_SCRIPTS_DIR = $SUB_SCRIPTS_DIR
87
+ $env:{app_caps}_SUB_TMP_DIR = $SUB_TMP_DIR
88
+ $env:{app_caps}_SUB_LOG_DIR = $SUB_LOG_DIR
89
+ $env:{app_caps}_SUB_STD_DIR = $SUB_STD_DIR
90
+ $env:{app_caps}_LOG_PATH = Join-Path $SUB_LOG_DIR "js_$JS_IDX.log"
91
+ $env:{app_caps}_JS_FUNCS_PATH = $JS_FUNCS_PATH
92
+ $env:{app_caps}_JS_IDX = {js_idx}
93
+ $env:{app_caps}_RUN_ID_FILE = $EAR_ID_FILE
94
+ """
95
+ )
96
+ #: Template for the jobscript header when directly executed.
97
+ JS_DIRECT_HEADER: ClassVar[str] = dedent(
98
+ """\
99
+ {shebang}
100
+ {header}
101
+ {wait_command}
102
+ """
103
+ )
104
+ #: Template for enabling writing of the app log.
105
+ JS_RUN_LOG_PATH_ENABLE: ClassVar[str] = 'Join-Path $SUB_LOG_DIR "{run_log_file_name}"'
106
+ #: Template for disabling writing of the app log.
107
+ JS_RUN_LOG_PATH_DISABLE: ClassVar[str] = '" "'
108
+ #: Template for the run execution command.
109
+ JS_RUN_CMD: ClassVar[str] = (
110
+ "{workflow_app_alias} internal workflow $WK_PATH execute-run "
111
+ "$SUB_IDX $JS_IDX $block_idx $block_act_idx $EAR_ID\n"
112
+ )
113
+ #: Template for the execution command for multiple combined runs.
114
+ JS_RUN_CMD_COMBINED: ClassVar[str] = (
115
+ "{workflow_app_alias} internal workflow $WK_PATH execute-combined-runs "
116
+ "$SUB_IDX $JS_IDX\n"
117
+ )
118
+ #: Template for setting up run environment variables and executing the run.
119
+ JS_RUN: ClassVar[str] = dedent(
120
+ """\
121
+ $EAR_ID = ($elem_EAR_IDs -split "{EAR_files_delimiter}")[$block_act_idx]
122
+ if ($EAR_ID -eq -1) {{
123
+ continue
124
+ }}
125
+
126
+ $env:{app_caps}_RUN_ID = $EAR_ID
127
+ $env:{app_caps}_RUN_LOG_PATH = {run_log_enable_disable}
128
+ $env:{app_caps}_LOG_PATH = $env:{app_caps}_RUN_LOG_PATH
129
+ $env:{app_caps}_RUN_STD_PATH = Join-Path $SUB_STD_DIR "$env:{app_caps}_RUN_ID.txt"
130
+ $env:{app_caps}_BLOCK_ACT_IDX = $block_act_idx
131
+
132
+ Set-Location $SUB_TMP_DIR
133
+
134
+ {run_cmd}
135
+ """
136
+ )
137
+ #: Template for the action-run processing loop in a jobscript.
138
+ JS_ACT_MULTI: ClassVar[str] = dedent(
139
+ """\
140
+ for ($block_act_idx = 0; $block_act_idx -lt {num_actions}; $block_act_idx += 1) {{
141
+ {run_block}
142
+ }}
143
+ """
144
+ )
145
+ #: Template for the single-action-run execution in a jobscript.
146
+ JS_ACT_SINGLE: ClassVar[str] = dedent(
147
+ """\
148
+ $block_act_idx = 0
149
+ {run_block}
150
+ """
151
+ )
152
+ #: Template for setting up environment variables and running one or more action-runs.
153
+ JS_MAIN: ClassVar[str] = dedent(
154
+ """\
155
+ $block_elem_idx = ($JS_elem_idx - {block_start_elem_idx})
156
+ $elem_EAR_IDs = get_nth_line $EAR_ID_FILE $JS_elem_idx
157
+ $env:{app_caps}_JS_ELEM_IDX = $JS_elem_idx
158
+ $env:{app_caps}_BLOCK_ELEM_IDX = $block_elem_idx
159
+
160
+ {action}
161
+ """
162
+ )
163
+ #: Template for a jobscript-block header.
164
+ JS_BLOCK_HEADER: ClassVar[str] = dedent( # for single-block jobscripts only
165
+ """\
166
+ $block_idx = 0
167
+ $env:{app_caps}_BLOCK_IDX = 0
168
+ """
169
+ )
170
+ #: Template for single-element execution.
171
+ JS_ELEMENT_SINGLE: ClassVar[str] = dedent(
172
+ """\
173
+ $JS_elem_idx = {block_start_elem_idx}
174
+ {main}
175
+ """
176
+ )
177
+ #: Template for the element processing loop in a jobscript.
178
+ JS_ELEMENT_MULTI_LOOP: ClassVar[str] = dedent(
179
+ """\
180
+ for ($JS_elem_idx = {block_start_elem_idx}; $JS_elem_idx -lt ({block_start_elem_idx} + {num_elements}); $JS_elem_idx += 1) {{
181
+ {main}
182
+ }}
183
+ """
184
+ )
185
+ #: Template for the jobscript block loop in a jobscript.
186
+ JS_BLOCK_LOOP: ClassVar[str] = dedent(
187
+ """\
188
+ $num_elements = {num_elements}
189
+ $num_actions = {num_actions}
190
+ $block_start_elem_idx = 0
191
+ for ($block_idx = 0; $block_idx -lt {num_blocks}; $block_idx += 1 ) {{
192
+ $env:{app_caps}_BLOCK_IDX = $block_idx
193
+ {element_loop}
194
+ $block_start_elem_idx += $num_elements[$block_idx]
195
+ }}
196
+ """
197
+ )
198
+ #: Template for the jobscript footer.
199
+ JS_FOOTER: ClassVar[str] = dedent(
200
+ """\
201
+ Set-Location $WK_PATH
202
+ """
203
+ )
204
+
205
+ def get_direct_submit_command(self, js_path: str) -> list[str]:
206
+ """Get the command for submitting a non-scheduled jobscript."""
207
+ return [*self.executable, "-File", js_path]
208
+
209
+ def get_command_file_launch_command(self, cmd_file_path: str) -> list[str]:
210
+ """Get the command for launching the commands file for a given run."""
211
+ # note the "-File" argument is required for the correct exit code to be recorded.
212
+ return [*self.executable, "-File", cmd_file_path]
213
+
214
+ @override
215
+ def get_version_info(self, exclude_os: bool = False) -> VersionInfo:
216
+ """Get powershell version information.
217
+
218
+ Parameters
219
+ ----------
220
+ exclude_os
221
+ If True, exclude operating system information.
222
+
223
+ """
224
+
225
+ proc = subprocess.run(
226
+ args=self.executable + ["-Command", "$PSVersionTable.PSVersion.ToString()"],
227
+ stdout=subprocess.PIPE,
228
+ text=True,
229
+ )
230
+ if proc.returncode == 0:
231
+ PS_version = proc.stdout.strip()
232
+ else:
233
+ raise RuntimeError("Failed to parse PowerShell version information.")
234
+
235
+ osinfo = {} if exclude_os else get_OS_info_windows()
236
+ return {
237
+ "shell_name": "powershell",
238
+ "shell_executable": self.executable,
239
+ "shell_version": PS_version,
240
+ **osinfo,
241
+ }
242
+
243
+ @staticmethod
244
+ def process_app_invoc_executable(app_invoc_exe: str) -> str:
245
+ if " " in app_invoc_exe:
246
+ # use call operator and single-quote the executable path:
247
+ app_invoc_exe = f"& '{app_invoc_exe}'"
248
+ return app_invoc_exe
249
+
250
+ @override
251
+ def format_env_var_get(self, var: str) -> str:
252
+ """
253
+ Format retrieval of a shell environment variable.
254
+ """
255
+ return f"$env:{var}"
256
+
257
+ @override
258
+ def format_array(self, lst: list) -> str:
259
+ """
260
+ Format construction of a shell array.
261
+ """
262
+ return "@(" + ", ".join(str(i) for i in lst) + ")"
263
+
264
+ @override
265
+ def format_array_get_item(self, arr_name: str, index: int | str) -> str:
266
+ """
267
+ Format retrieval of a shell array item at a specified index.
268
+ """
269
+ return f"${arr_name}[{index}]"
270
+
271
+ @override
272
+ def format_stream_assignment(self, shell_var_name: str, command: str) -> str:
273
+ """
274
+ Produce code to assign the output of the command to a shell variable.
275
+ """
276
+ return f"${shell_var_name} = {command}"
277
+
278
+ @override
279
+ def format_source_functions_file(self, app_name: str, commands: str) -> str:
280
+ """
281
+ Format sourcing (i.e. invocation) of the jobscript functions file.
282
+ """
283
+ app_caps = app_name.upper()
284
+ out = dedent(
285
+ """\
286
+ . $env:{app_name}_JS_FUNCS_PATH
287
+
288
+ """
289
+ ).format(app_name=app_caps)
290
+
291
+ var_strings = (
292
+ f"{app_caps}_WK_PATH",
293
+ f"{app_caps}_SUB_SCRIPTS_DIR",
294
+ f"{app_caps}_JS_IDX",
295
+ f"{app_caps}_BLOCK_IDX",
296
+ f"{app_caps}_BLOCK_ACT_IDX",
297
+ f"{app_caps}_RUN_ID",
298
+ f"{app_caps}_RUN_STD_PATH",
299
+ f"{app_caps}_RUN_SCRIPT_NAME",
300
+ f"{app_caps}_RUN_SCRIPT_NAME_NO_EXT",
301
+ f"{app_caps}_RUN_SCRIPT_DIR",
302
+ f"{app_caps}_RUN_SCRIPT_PATH",
303
+ f"{app_caps}_RUN_PROGRAM_NAME",
304
+ f"{app_caps}_RUN_PROGRAM_NAME_NO_EXT",
305
+ f"{app_caps}_RUN_PROGRAM_DIR",
306
+ f"{app_caps}_RUN_PROGRAM_PATH",
307
+ )
308
+ add = False
309
+ for i in var_strings:
310
+ if i in commands:
311
+ add = True
312
+ out += f"${i} = $env:{i}\n"
313
+
314
+ if add:
315
+ out += "\n"
316
+
317
+ return out
318
+
319
+ @override
320
+ def format_commands_file(self, app_name: str, commands: str) -> str:
321
+ """
322
+ Format the commands file.
323
+ """
324
+ return (
325
+ self.format_source_functions_file(app_name, commands)
326
+ + commands
327
+ + "\nexit $LASTEXITCODE\n"
328
+ )
329
+
330
+ @override
331
+ def format_save_parameter(
332
+ self,
333
+ workflow_app_alias: str,
334
+ param_name: str,
335
+ shell_var_name: str,
336
+ cmd_idx: int,
337
+ stderr: bool,
338
+ app_name: str,
339
+ ) -> str:
340
+ """
341
+ Produce code to save a parameter's value into the workflow persistent store.
342
+ """
343
+ # TODO: quote shell_var_name as well? e.g. if it's a white-space delimited list?
344
+ # and test.
345
+ stderr_str = " --stderr" if stderr else ""
346
+ app_caps = app_name.upper()
347
+ return (
348
+ f'{workflow_app_alias} --std-stream "${app_caps}_RUN_STD_PATH" '
349
+ f'internal workflow "${app_caps}_WK_PATH" save-parameter {stderr_str}'
350
+ f'"--" {param_name} ${shell_var_name} ${app_caps}_RUN_ID {cmd_idx}'
351
+ f"\n"
352
+ )