hpcflow-new2 0.2.0a190__py3-none-any.whl → 0.2.0a200__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 (132) hide show
  1. hpcflow/__pyinstaller/hook-hpcflow.py +1 -0
  2. hpcflow/_version.py +1 -1
  3. hpcflow/data/scripts/bad_script.py +2 -0
  4. hpcflow/data/scripts/do_nothing.py +2 -0
  5. hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
  6. hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
  7. hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
  8. hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
  9. hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
  10. hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
  11. hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
  12. hpcflow/data/scripts/input_file_generator_basic.py +3 -0
  13. hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
  14. hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
  15. hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
  16. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
  17. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
  18. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
  19. hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
  20. hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
  21. hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
  22. hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
  23. hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
  24. hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
  25. hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
  26. hpcflow/data/scripts/output_file_parser_basic.py +3 -0
  27. hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
  28. hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
  29. hpcflow/data/scripts/script_exit_test.py +5 -0
  30. hpcflow/data/template_components/environments.yaml +1 -1
  31. hpcflow/sdk/__init__.py +5 -0
  32. hpcflow/sdk/app.py +166 -92
  33. hpcflow/sdk/cli.py +263 -84
  34. hpcflow/sdk/cli_common.py +99 -5
  35. hpcflow/sdk/config/callbacks.py +38 -1
  36. hpcflow/sdk/config/config.py +102 -13
  37. hpcflow/sdk/config/errors.py +19 -5
  38. hpcflow/sdk/config/types.py +3 -0
  39. hpcflow/sdk/core/__init__.py +25 -1
  40. hpcflow/sdk/core/actions.py +914 -262
  41. hpcflow/sdk/core/cache.py +76 -34
  42. hpcflow/sdk/core/command_files.py +14 -128
  43. hpcflow/sdk/core/commands.py +35 -6
  44. hpcflow/sdk/core/element.py +122 -50
  45. hpcflow/sdk/core/errors.py +58 -2
  46. hpcflow/sdk/core/execute.py +207 -0
  47. hpcflow/sdk/core/loop.py +408 -50
  48. hpcflow/sdk/core/loop_cache.py +4 -4
  49. hpcflow/sdk/core/parameters.py +382 -37
  50. hpcflow/sdk/core/run_dir_files.py +13 -40
  51. hpcflow/sdk/core/skip_reason.py +7 -0
  52. hpcflow/sdk/core/task.py +119 -30
  53. hpcflow/sdk/core/task_schema.py +68 -0
  54. hpcflow/sdk/core/test_utils.py +66 -27
  55. hpcflow/sdk/core/types.py +54 -1
  56. hpcflow/sdk/core/utils.py +136 -19
  57. hpcflow/sdk/core/workflow.py +1587 -356
  58. hpcflow/sdk/data/workflow_spec_schema.yaml +2 -0
  59. hpcflow/sdk/demo/cli.py +7 -0
  60. hpcflow/sdk/helper/cli.py +1 -0
  61. hpcflow/sdk/log.py +42 -15
  62. hpcflow/sdk/persistence/base.py +405 -53
  63. hpcflow/sdk/persistence/json.py +177 -52
  64. hpcflow/sdk/persistence/pending.py +237 -69
  65. hpcflow/sdk/persistence/store_resource.py +3 -2
  66. hpcflow/sdk/persistence/types.py +15 -4
  67. hpcflow/sdk/persistence/zarr.py +928 -81
  68. hpcflow/sdk/submission/jobscript.py +1408 -489
  69. hpcflow/sdk/submission/schedulers/__init__.py +40 -5
  70. hpcflow/sdk/submission/schedulers/direct.py +33 -19
  71. hpcflow/sdk/submission/schedulers/sge.py +51 -16
  72. hpcflow/sdk/submission/schedulers/slurm.py +44 -16
  73. hpcflow/sdk/submission/schedulers/utils.py +7 -2
  74. hpcflow/sdk/submission/shells/base.py +68 -20
  75. hpcflow/sdk/submission/shells/bash.py +222 -129
  76. hpcflow/sdk/submission/shells/powershell.py +200 -150
  77. hpcflow/sdk/submission/submission.py +852 -119
  78. hpcflow/sdk/submission/types.py +18 -21
  79. hpcflow/sdk/typing.py +24 -5
  80. hpcflow/sdk/utils/arrays.py +71 -0
  81. hpcflow/sdk/utils/deferred_file.py +55 -0
  82. hpcflow/sdk/utils/hashing.py +16 -0
  83. hpcflow/sdk/utils/patches.py +12 -0
  84. hpcflow/sdk/utils/strings.py +33 -0
  85. hpcflow/tests/api/test_api.py +32 -0
  86. hpcflow/tests/conftest.py +19 -0
  87. hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
  88. hpcflow/tests/data/multi_path_sequences.yaml +29 -0
  89. hpcflow/tests/data/workflow_test_run_abort.yaml +34 -35
  90. hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
  91. hpcflow/tests/scripts/test_input_file_generators.py +282 -0
  92. hpcflow/tests/scripts/test_main_scripts.py +821 -70
  93. hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
  94. hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
  95. hpcflow/tests/shells/wsl/test_wsl_submission.py +6 -0
  96. hpcflow/tests/unit/test_action.py +176 -0
  97. hpcflow/tests/unit/test_app.py +20 -0
  98. hpcflow/tests/unit/test_cache.py +46 -0
  99. hpcflow/tests/unit/test_cli.py +133 -0
  100. hpcflow/tests/unit/test_config.py +122 -1
  101. hpcflow/tests/unit/test_element_iteration.py +47 -0
  102. hpcflow/tests/unit/test_jobscript_unit.py +757 -0
  103. hpcflow/tests/unit/test_loop.py +1332 -27
  104. hpcflow/tests/unit/test_meta_task.py +325 -0
  105. hpcflow/tests/unit/test_multi_path_sequences.py +229 -0
  106. hpcflow/tests/unit/test_parameter.py +13 -0
  107. hpcflow/tests/unit/test_persistence.py +190 -8
  108. hpcflow/tests/unit/test_run.py +109 -3
  109. hpcflow/tests/unit/test_run_directories.py +29 -0
  110. hpcflow/tests/unit/test_shell.py +20 -0
  111. hpcflow/tests/unit/test_submission.py +5 -76
  112. hpcflow/tests/unit/test_workflow_template.py +31 -0
  113. hpcflow/tests/unit/utils/test_arrays.py +40 -0
  114. hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
  115. hpcflow/tests/unit/utils/test_hashing.py +65 -0
  116. hpcflow/tests/unit/utils/test_patches.py +5 -0
  117. hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
  118. hpcflow/tests/workflows/__init__.py +0 -0
  119. hpcflow/tests/workflows/test_directory_structure.py +31 -0
  120. hpcflow/tests/workflows/test_jobscript.py +332 -0
  121. hpcflow/tests/workflows/test_run_status.py +198 -0
  122. hpcflow/tests/workflows/test_skip_downstream.py +696 -0
  123. hpcflow/tests/workflows/test_submission.py +140 -0
  124. hpcflow/tests/workflows/test_workflows.py +142 -2
  125. hpcflow/tests/workflows/test_zip.py +18 -0
  126. hpcflow/viz_demo.ipynb +6587 -3
  127. {hpcflow_new2-0.2.0a190.dist-info → hpcflow_new2-0.2.0a200.dist-info}/METADATA +7 -4
  128. hpcflow_new2-0.2.0a200.dist-info/RECORD +222 -0
  129. hpcflow_new2-0.2.0a190.dist-info/RECORD +0 -165
  130. {hpcflow_new2-0.2.0a190.dist-info → hpcflow_new2-0.2.0a200.dist-info}/LICENSE +0 -0
  131. {hpcflow_new2-0.2.0a190.dist-info → hpcflow_new2-0.2.0a200.dist-info}/WHEEL +0 -0
  132. {hpcflow_new2-0.2.0a190.dist-info → hpcflow_new2-0.2.0a200.dist-info}/entry_points.txt +0 -0
@@ -4,11 +4,10 @@ Shell models based on Microsoft PowerShell.
4
4
 
5
5
  from __future__ import annotations
6
6
  import subprocess
7
- from textwrap import dedent, indent
7
+ from textwrap import dedent
8
8
  from typing import TYPE_CHECKING
9
9
  from typing_extensions import override
10
10
  from hpcflow.sdk.typing import hydrate
11
- from hpcflow.sdk.core import ABORT_EXIT_CODE
12
11
  from hpcflow.sdk.submission.shells.base import Shell
13
12
  from hpcflow.sdk.submission.shells.os_version import get_OS_info_windows
14
13
 
@@ -34,13 +33,13 @@ class WindowsPowerShell(Shell):
34
33
  JS_ENV_SETUP_INDENT: ClassVar[str] = 2 * JS_INDENT
35
34
  #: Template for the jobscript shebang line.
36
35
  JS_SHEBANG: ClassVar[str] = ""
37
- #: Template for the common part of the jobscript header.
38
- JS_HEADER: ClassVar[str] = dedent(
36
+ #: Template for the jobscript functions file.
37
+ JS_FUNCS: ClassVar[str] = dedent(
39
38
  """\
40
39
  function {workflow_app_alias} {{
41
40
  & {{
42
41
  {env_setup}{app_invoc} `
43
- --with-config log_file_path "$pwd/{run_log_file}" `
42
+ --with-config log_file_path "$env:{app_caps}_LOG_PATH" `
44
43
  --config-dir "{config_dir}" `
45
44
  --config-key "{config_invoc_key}" `
46
45
  $args
@@ -50,6 +49,12 @@ class WindowsPowerShell(Shell):
50
49
  function get_nth_line($file, $line) {{
51
50
  Get-Content $file | Select-Object -Skip $line -First 1
52
51
  }}
52
+ """
53
+ )
54
+ #: Template for the common part of the jobscript header.
55
+ JS_HEADER: ClassVar[str] = dedent(
56
+ """\
57
+ $ErrorActionPreference = 'Stop'
53
58
 
54
59
  function JoinMultiPath {{
55
60
  $numArgs = $args.Length
@@ -58,90 +63,141 @@ class WindowsPowerShell(Shell):
58
63
  $path = Join-Path $path $args[$i]
59
64
  }}
60
65
  return $path
61
- }}
62
-
63
- function StartJobHere($block) {{
64
- $jobInitBlock = [scriptblock]::Create(@"
65
- Function wkflow_app {{ $function:wkflow_app }}
66
- Function get_nth_line {{ $function:get_nth_line }}
67
- Function JoinMultiPath {{ $function:JoinMultiPath }}
68
- Set-Location '$pwd'
69
- "@)
70
- Start-Job -InitializationScript $jobInitBlock -Script $block
71
- }}
66
+ }}
72
67
 
73
68
  $WK_PATH = $(Get-Location)
74
69
  $WK_PATH_ARG = $WK_PATH
75
70
  $SUB_IDX = {sub_idx}
76
71
  $JS_IDX = {js_idx}
77
- $EAR_ID_FILE = JoinMultiPath $WK_PATH artifacts submissions $SUB_IDX {EAR_file_name}
78
- $ELEM_RUN_DIR_FILE = JoinMultiPath $WK_PATH artifacts submissions $SUB_IDX {element_run_dirs_file_path}
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
79
94
  """
80
95
  )
81
96
  #: Template for the jobscript header when directly executed.
82
97
  JS_DIRECT_HEADER: ClassVar[str] = dedent(
83
98
  """\
84
99
  {shebang}
85
-
86
100
  {header}
87
101
  {wait_command}
88
102
  """
89
103
  )
90
- #: Template for the jobscript body.
91
- JS_MAIN: ClassVar[str] = dedent(
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(
92
120
  """\
93
- $elem_EAR_IDs = get_nth_line $EAR_ID_FILE $JS_elem_idx
94
- $elem_run_dirs = get_nth_line $ELEM_RUN_DIR_FILE $JS_elem_idx
95
-
96
- for ($JS_act_idx = 0; $JS_act_idx -lt {num_actions}; $JS_act_idx += 1) {{
97
-
98
- $EAR_ID = ($elem_EAR_IDs -split "{EAR_files_delimiter}")[$JS_act_idx]
99
- if ($EAR_ID -eq -1) {{
100
- continue
101
- }}
102
-
103
- $run_dir = ($elem_run_dirs -split "{EAR_files_delimiter}")[$JS_act_idx]
104
- $run_dir_abs = "$WK_PATH\\$run_dir"
105
- Set-Location $run_dir_abs
106
- $app_stream_file = "$pwd/{run_stream_file}"
107
-
108
- $skip = {workflow_app_alias} internal workflow $WK_PATH get-ear-skipped $EAR_ID 2>> $app_stream_file
109
- $exc_sk = $LASTEXITCODE
110
-
111
- if ($exc_sk -eq 0) {{
112
- if ($skip -eq "1") {{
113
- continue
114
- }}
115
-
116
- {workflow_app_alias} internal workflow $WK_PATH write-commands $SUB_IDX $JS_IDX $JS_act_idx $EAR_ID 2>&1 >> $app_stream_file
117
- $exc_wc = $LASTEXITCODE
121
+ $EAR_ID = ($elem_EAR_IDs -split "{EAR_files_delimiter}")[$block_act_idx]
122
+ if ($EAR_ID -eq -1) {{
123
+ continue
124
+ }}
118
125
 
119
- {workflow_app_alias} internal workflow $WK_PATH set-ear-start $EAR_ID 2>&1 >> $app_stream_file
120
- $exc_se = $LASTEXITCODE
121
-
122
- if (($exc_wc -eq 0) -and ($exc_se -eq 0)) {{
123
- . (Join-Path $run_dir_abs "{commands_file_name}")
124
- $exit_code = $LASTEXITCODE
125
- }}
126
- else {{
127
- $exit_code = If ($exc_wc -ne 0) {{$exc_wc}} Else {{$exc_se}}
128
- }}
129
- }}
130
- else {{
131
- $exit_code = $exc_sk
132
- }}
133
- $global:LASTEXITCODE = $null
134
- {workflow_app_alias} internal workflow $WK_PATH set-ear-end $JS_IDX $JS_act_idx $EAR_ID "--" "$exit_code" 2>&1 >> $app_stream_file
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
135
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}
136
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}
137
175
  """
138
176
  )
139
177
  #: Template for the element processing loop in a jobscript.
140
- JS_ELEMENT_LOOP: ClassVar[str] = dedent(
178
+ JS_ELEMENT_MULTI_LOOP: ClassVar[str] = dedent(
141
179
  """\
142
- for ($JS_elem_idx = 0; $JS_elem_idx -lt {num_elements}; $JS_elem_idx += 1) {{
180
+ for ($JS_elem_idx = {block_start_elem_idx}; $JS_elem_idx -lt ({block_start_elem_idx} + {num_elements}); $JS_elem_idx += 1) {{
143
181
  {main}
144
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
+ """\
145
201
  Set-Location $WK_PATH
146
202
  """
147
203
  )
@@ -150,6 +206,11 @@ class WindowsPowerShell(Shell):
150
206
  """Get the command for submitting a non-scheduled jobscript."""
151
207
  return [*self.executable, "-File", js_path]
152
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
+
153
214
  @override
154
215
  def get_version_info(self, exclude_os: bool = False) -> VersionInfo:
155
216
  """Get powershell version information.
@@ -162,7 +223,7 @@ class WindowsPowerShell(Shell):
162
223
  """
163
224
 
164
225
  proc = subprocess.run(
165
- args=self.executable + ["$PSVersionTable.PSVersion.ToString()"],
226
+ args=self.executable + ["-Command", "$PSVersionTable.PSVersion.ToString()"],
166
227
  stdout=subprocess.PIPE,
167
228
  text=True,
168
229
  )
@@ -186,6 +247,27 @@ class WindowsPowerShell(Shell):
186
247
  app_invoc_exe = f"& '{app_invoc_exe}'"
187
248
  return app_invoc_exe
188
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
+
189
271
  @override
190
272
  def format_stream_assignment(self, shell_var_name: str, command: str) -> str:
191
273
  """
@@ -193,15 +275,63 @@ class WindowsPowerShell(Shell):
193
275
  """
194
276
  return f"${shell_var_name} = {command}"
195
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
+ )
304
+ add = False
305
+ for i in var_strings:
306
+ if i in commands:
307
+ add = True
308
+ out += f"${i} = $env:{i}\n"
309
+
310
+ if add:
311
+ out += "\n"
312
+
313
+ return out
314
+
315
+ @override
316
+ def format_commands_file(self, app_name: str, commands: str) -> str:
317
+ """
318
+ Format the commands file.
319
+ """
320
+ return (
321
+ self.format_source_functions_file(app_name, commands)
322
+ + commands
323
+ + "\nexit $LASTEXITCODE\n"
324
+ )
325
+
196
326
  @override
197
327
  def format_save_parameter(
198
328
  self,
199
329
  workflow_app_alias: str,
200
330
  param_name: str,
201
331
  shell_var_name: str,
202
- EAR_ID: int,
203
332
  cmd_idx: int,
204
333
  stderr: bool,
334
+ app_name: str,
205
335
  ) -> str:
206
336
  """
207
337
  Produce code to save a parameter's value into the workflow persistent store.
@@ -209,90 +339,10 @@ class WindowsPowerShell(Shell):
209
339
  # TODO: quote shell_var_name as well? e.g. if it's a white-space delimited list?
210
340
  # and test.
211
341
  stderr_str = " --stderr" if stderr else ""
342
+ app_caps = app_name.upper()
212
343
  return (
213
- f"{workflow_app_alias} "
214
- f"internal workflow $WK_PATH save-parameter "
215
- f"{param_name} ${shell_var_name} {EAR_ID} {cmd_idx}{stderr_str} "
216
- f"2>&1 >> $app_stream_file"
217
- f"\n"
218
- )
219
-
220
- @override
221
- def format_loop_check(
222
- self, workflow_app_alias: str, loop_name: str, run_ID: int
223
- ) -> str:
224
- """
225
- Produce code to check the looping status of part of a workflow.
226
- """
227
- return (
228
- f"{workflow_app_alias} "
229
- f"internal workflow $WK_PATH check-loop "
230
- f"{loop_name} {run_ID} "
231
- f"2>&1 >> $app_stream_file"
344
+ f'{workflow_app_alias} --std-stream "${app_caps}_RUN_STD_PATH" '
345
+ f'internal workflow "${app_caps}_WK_PATH" save-parameter {stderr_str}'
346
+ f'"--" {param_name} ${shell_var_name} ${app_caps}_RUN_ID {cmd_idx}'
232
347
  f"\n"
233
348
  )
234
-
235
- @override
236
- def wrap_in_subshell(self, commands: str, abortable: bool) -> str:
237
- """Format commands to run within a child scope.
238
-
239
- This assumes `commands` ends in a newline.
240
-
241
- """
242
- commands = indent(commands, self.JS_INDENT)
243
- if abortable:
244
- # run commands as a background job, and poll a file to check for abort
245
- # requests:
246
- return dedent(
247
- """\
248
- $job = StartJobHere {{
249
- $WK_PATH = $using:WK_PATH
250
- $SUB_IDX = $using:SUB_IDX
251
- $JS_IDX = $using:JS_IDX
252
- $EAR_ID = $using:EAR_ID
253
- $app_stream_file= $using:app_stream_file
254
-
255
- {commands}
256
- if ($LASTEXITCODE -ne 0) {{
257
- throw
258
- }}
259
- }}
260
-
261
- $is_abort = $null
262
- $abort_file = JoinMultiPath $WK_PATH artifacts submissions $SUB_IDX abort_EARs.txt
263
- while ($true) {{
264
- $is_abort = get_nth_line $abort_file $EAR_ID
265
- if ($job.State -ne "Running") {{
266
- break
267
- }}
268
- elseif ($is_abort -eq "1") {{
269
- Add-Content -Path $app_stream_file -Value "Abort instruction received; stopping commands..."
270
- Stop-Job -Job $job
271
- Wait-Job -Job $job
272
- break
273
- }}
274
- else {{
275
- Receive-Job -job $job | Write-Output
276
- Start-Sleep 1 # TODO: TEMP: increase for production
277
- }}
278
- }}
279
- Receive-Job -job $job | Write-Output
280
- if ($job.state -eq "Completed") {{
281
- exit 0
282
- }}
283
- elseif ($is_abort -eq "1") {{
284
- exit {abort_exit_code}
285
- }}
286
- else {{
287
- exit 1
288
- }}
289
- """
290
- ).format(commands=commands, abort_exit_code=ABORT_EXIT_CODE)
291
- else:
292
- # run commands in "foreground":
293
- return dedent(
294
- """\
295
- & {{
296
- {commands}}}
297
- """
298
- ).format(commands=commands)