hpcflow-new2 0.2.0a189__py3-none-any.whl → 0.2.0a199__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.
- hpcflow/__pyinstaller/hook-hpcflow.py +9 -6
- hpcflow/_version.py +1 -1
- hpcflow/app.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/environments.yaml +1 -1
- hpcflow/sdk/__init__.py +26 -15
- hpcflow/sdk/app.py +2192 -768
- hpcflow/sdk/cli.py +506 -296
- hpcflow/sdk/cli_common.py +105 -7
- hpcflow/sdk/config/__init__.py +1 -1
- hpcflow/sdk/config/callbacks.py +115 -43
- hpcflow/sdk/config/cli.py +126 -103
- hpcflow/sdk/config/config.py +674 -318
- hpcflow/sdk/config/config_file.py +131 -95
- hpcflow/sdk/config/errors.py +125 -84
- hpcflow/sdk/config/types.py +148 -0
- hpcflow/sdk/core/__init__.py +25 -1
- hpcflow/sdk/core/actions.py +1771 -1059
- hpcflow/sdk/core/app_aware.py +24 -0
- hpcflow/sdk/core/cache.py +139 -79
- hpcflow/sdk/core/command_files.py +263 -287
- hpcflow/sdk/core/commands.py +145 -112
- hpcflow/sdk/core/element.py +828 -535
- hpcflow/sdk/core/enums.py +192 -0
- hpcflow/sdk/core/environment.py +74 -93
- hpcflow/sdk/core/errors.py +455 -52
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +540 -272
- hpcflow/sdk/core/loop.py +751 -347
- hpcflow/sdk/core/loop_cache.py +164 -47
- hpcflow/sdk/core/object_list.py +370 -207
- hpcflow/sdk/core/parameters.py +1100 -627
- hpcflow/sdk/core/rule.py +59 -41
- hpcflow/sdk/core/run_dir_files.py +21 -37
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +1649 -1339
- hpcflow/sdk/core/task_schema.py +308 -196
- hpcflow/sdk/core/test_utils.py +191 -114
- hpcflow/sdk/core/types.py +440 -0
- hpcflow/sdk/core/utils.py +485 -309
- hpcflow/sdk/core/validation.py +82 -9
- hpcflow/sdk/core/workflow.py +2544 -1178
- hpcflow/sdk/core/zarr_io.py +98 -137
- hpcflow/sdk/data/workflow_spec_schema.yaml +2 -0
- hpcflow/sdk/demo/cli.py +53 -33
- hpcflow/sdk/helper/cli.py +18 -15
- hpcflow/sdk/helper/helper.py +75 -63
- hpcflow/sdk/helper/watcher.py +61 -28
- hpcflow/sdk/log.py +122 -71
- hpcflow/sdk/persistence/__init__.py +8 -31
- hpcflow/sdk/persistence/base.py +1360 -606
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +38 -0
- hpcflow/sdk/persistence/json.py +568 -188
- hpcflow/sdk/persistence/pending.py +382 -179
- hpcflow/sdk/persistence/store_resource.py +39 -23
- hpcflow/sdk/persistence/types.py +318 -0
- hpcflow/sdk/persistence/utils.py +14 -11
- hpcflow/sdk/persistence/zarr.py +1337 -433
- hpcflow/sdk/runtime.py +44 -41
- hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
- hpcflow/sdk/submission/jobscript.py +1651 -692
- hpcflow/sdk/submission/schedulers/__init__.py +167 -39
- hpcflow/sdk/submission/schedulers/direct.py +121 -81
- hpcflow/sdk/submission/schedulers/sge.py +170 -129
- hpcflow/sdk/submission/schedulers/slurm.py +291 -268
- hpcflow/sdk/submission/schedulers/utils.py +12 -2
- hpcflow/sdk/submission/shells/__init__.py +14 -15
- hpcflow/sdk/submission/shells/base.py +150 -29
- hpcflow/sdk/submission/shells/bash.py +283 -173
- hpcflow/sdk/submission/shells/os_version.py +31 -30
- hpcflow/sdk/submission/shells/powershell.py +228 -170
- hpcflow/sdk/submission/submission.py +1014 -335
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +182 -12
- hpcflow/sdk/utils/arrays.py +71 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +12 -0
- hpcflow/sdk/utils/strings.py +33 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +27 -6
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +34 -35
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +866 -85
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +12 -4
- hpcflow/tests/unit/test_action.py +262 -75
- hpcflow/tests/unit/test_action_rule.py +9 -4
- hpcflow/tests/unit/test_app.py +33 -6
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +134 -1
- hpcflow/tests/unit/test_command.py +71 -54
- hpcflow/tests/unit/test_config.py +142 -16
- hpcflow/tests/unit/test_config_file.py +21 -18
- hpcflow/tests/unit/test_element.py +58 -62
- hpcflow/tests/unit/test_element_iteration.py +50 -1
- hpcflow/tests/unit/test_element_set.py +29 -19
- hpcflow/tests/unit/test_group.py +4 -2
- hpcflow/tests/unit/test_input_source.py +116 -93
- hpcflow/tests/unit/test_input_value.py +29 -24
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +44 -35
- hpcflow/tests/unit/test_loop.py +1396 -84
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +229 -0
- hpcflow/tests/unit/test_object_list.py +17 -12
- hpcflow/tests/unit/test_parameter.py +29 -7
- hpcflow/tests/unit/test_persistence.py +237 -42
- hpcflow/tests/unit/test_resources.py +20 -18
- hpcflow/tests/unit/test_run.py +117 -6
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +2 -1
- hpcflow/tests/unit/test_schema_input.py +23 -15
- hpcflow/tests/unit/test_shell.py +23 -2
- hpcflow/tests/unit/test_slurm.py +8 -7
- hpcflow/tests/unit/test_submission.py +38 -89
- hpcflow/tests/unit/test_task.py +352 -247
- hpcflow/tests/unit/test_task_schema.py +33 -20
- hpcflow/tests/unit/test_utils.py +9 -11
- hpcflow/tests/unit/test_value_sequence.py +15 -12
- hpcflow/tests/unit/test_workflow.py +114 -83
- hpcflow/tests/unit/test_workflow_template.py +0 -1
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +334 -1
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +160 -15
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6587 -3
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/METADATA +8 -4
- hpcflow_new2-0.2.0a199.dist-info/RECORD +221 -0
- hpcflow/sdk/core/parallel.py +0 -21
- hpcflow_new2-0.2.0a189.dist-info/RECORD +0 -158
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/entry_points.txt +0 -0
@@ -2,16 +2,17 @@
|
|
2
2
|
Operating system information discovery helpers.
|
3
3
|
"""
|
4
4
|
|
5
|
-
import
|
5
|
+
from __future__ import annotations
|
6
|
+
from collections.abc import Mapping
|
6
7
|
import platform
|
7
8
|
import re
|
8
9
|
import subprocess
|
9
|
-
from typing import
|
10
|
+
from typing import Final
|
10
11
|
|
11
|
-
|
12
|
+
_DEFAULT_LINUX_RELEASE_FILE: Final = "/etc/os-release"
|
12
13
|
|
13
14
|
|
14
|
-
def get_OS_info() ->
|
15
|
+
def get_OS_info() -> Mapping[str, str]:
|
15
16
|
"""
|
16
17
|
Get basic operating system version info.
|
17
18
|
"""
|
@@ -23,7 +24,7 @@ def get_OS_info() -> Dict:
|
|
23
24
|
}
|
24
25
|
|
25
26
|
|
26
|
-
def get_OS_info_windows() ->
|
27
|
+
def get_OS_info_windows() -> Mapping[str, str]:
|
27
28
|
"""
|
28
29
|
Get operating system version info: Windows version.
|
29
30
|
"""
|
@@ -31,10 +32,10 @@ def get_OS_info_windows() -> Dict:
|
|
31
32
|
|
32
33
|
|
33
34
|
def get_OS_info_POSIX(
|
34
|
-
WSL_executable:
|
35
|
-
use_py:
|
36
|
-
linux_release_file:
|
37
|
-
) ->
|
35
|
+
WSL_executable: list[str] | None = None,
|
36
|
+
use_py: bool = True,
|
37
|
+
linux_release_file: str | None = None,
|
38
|
+
) -> Mapping[str, str]:
|
38
39
|
"""
|
39
40
|
Get operating system version info: POSIX version.
|
40
41
|
|
@@ -48,11 +49,11 @@ def get_OS_info_POSIX(
|
|
48
49
|
when getting OS info in WSL on Windows, since we need to call the WSL executable.
|
49
50
|
linux_release_file:
|
50
51
|
If on Linux, record the name and version fields from this file.
|
51
|
-
|
52
52
|
"""
|
53
53
|
|
54
|
-
def try_subprocess_call(
|
54
|
+
def try_subprocess_call(*args: str) -> str:
|
55
55
|
exc = None
|
56
|
+
command = [*WSL_exe, *args]
|
56
57
|
try:
|
57
58
|
proc = subprocess.run(
|
58
59
|
args=command,
|
@@ -63,23 +64,21 @@ def get_OS_info_POSIX(
|
|
63
64
|
except Exception as err:
|
64
65
|
exc = err
|
65
66
|
|
66
|
-
if proc.returncode
|
67
|
+
if proc.returncode or exc:
|
67
68
|
raise RuntimeError(
|
68
69
|
f"Failed to get POSIX OS info. Command was: {command!r}. Subprocess "
|
69
70
|
f"exception was: {exc!r}. Stderr was: {proc.stderr!r}."
|
70
71
|
)
|
71
|
-
|
72
|
-
return proc.stdout
|
72
|
+
return proc.stdout
|
73
73
|
|
74
|
-
|
75
|
-
out = {}
|
74
|
+
WSL_exe = WSL_executable or []
|
75
|
+
out: dict[str, str] = {}
|
76
76
|
if use_py:
|
77
77
|
out.update(**get_OS_info())
|
78
|
-
|
79
78
|
else:
|
80
|
-
OS_name = try_subprocess_call(
|
81
|
-
OS_release = try_subprocess_call(
|
82
|
-
OS_version = try_subprocess_call(
|
79
|
+
OS_name = try_subprocess_call("uname", "-s").strip()
|
80
|
+
OS_release = try_subprocess_call("uname", "-r").strip()
|
81
|
+
OS_version = try_subprocess_call("uname", "-v").strip()
|
83
82
|
|
84
83
|
out["OS_name"] = OS_name
|
85
84
|
out["OS_release"] = OS_release
|
@@ -87,28 +86,30 @@ def get_OS_info_POSIX(
|
|
87
86
|
|
88
87
|
if out["OS_name"] == "Linux":
|
89
88
|
# get linux distribution name and version:
|
90
|
-
linux_release_file = linux_release_file or
|
91
|
-
release_out = try_subprocess_call(
|
89
|
+
linux_release_file = linux_release_file or _DEFAULT_LINUX_RELEASE_FILE
|
90
|
+
release_out = try_subprocess_call("cat", linux_release_file)
|
92
91
|
|
93
|
-
name_match =
|
94
|
-
if name_match:
|
95
|
-
lin_name = name_match.group(1)
|
96
|
-
else:
|
92
|
+
name_match = _NAME_RE.search(release_out)
|
93
|
+
if not name_match:
|
97
94
|
raise RuntimeError(
|
98
95
|
f"Failed to get Linux distribution name from file `{linux_release_file}`."
|
99
96
|
)
|
97
|
+
lin_name: str = name_match[1]
|
100
98
|
|
101
|
-
version_match =
|
102
|
-
if version_match:
|
103
|
-
lin_version = version_match.group(1)
|
104
|
-
else:
|
99
|
+
version_match = _VERSION_RE.search(release_out)
|
100
|
+
if not version_match:
|
105
101
|
raise RuntimeError(
|
106
102
|
f"Failed to get Linux distribution version from file "
|
107
103
|
f"`{linux_release_file}`."
|
108
104
|
)
|
105
|
+
lin_version: str = version_match[1]
|
109
106
|
|
110
107
|
out["linux_release_file"] = linux_release_file
|
111
108
|
out["linux_distribution_name"] = lin_name
|
112
109
|
out["linux_distribution_version"] = lin_version
|
113
110
|
|
114
111
|
return out
|
112
|
+
|
113
|
+
|
114
|
+
_NAME_RE: Final = re.compile(r"^NAME=\"(.*)\"", flags=re.MULTILINE)
|
115
|
+
_VERSION_RE: Final = re.compile(r"^VERSION=\"(.*)\"", flags=re.MULTILINE)
|
@@ -2,37 +2,44 @@
|
|
2
2
|
Shell models based on Microsoft PowerShell.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
5
6
|
import subprocess
|
6
|
-
from textwrap import dedent
|
7
|
-
from typing import
|
8
|
-
from
|
9
|
-
from hpcflow.sdk.
|
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
|
10
12
|
from hpcflow.sdk.submission.shells.os_version import get_OS_info_windows
|
11
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from typing import ClassVar
|
16
|
+
from .base import VersionInfo
|
12
17
|
|
18
|
+
|
19
|
+
@hydrate
|
13
20
|
class WindowsPowerShell(Shell):
|
14
21
|
"""Class to represent using PowerShell on Windows to generate and submit a jobscript."""
|
15
22
|
|
16
23
|
# TODO: add snippets that can be used in demo task schemas?
|
17
24
|
|
18
25
|
#: Default for executable name.
|
19
|
-
DEFAULT_EXE = "powershell.exe"
|
26
|
+
DEFAULT_EXE: ClassVar[str] = "powershell.exe"
|
20
27
|
|
21
28
|
#: File extension for jobscripts.
|
22
|
-
JS_EXT = ".ps1"
|
29
|
+
JS_EXT: ClassVar[str] = ".ps1"
|
23
30
|
#: Basic indent.
|
24
|
-
JS_INDENT = " "
|
31
|
+
JS_INDENT: ClassVar[str] = " "
|
25
32
|
#: Indent for environment setup.
|
26
|
-
JS_ENV_SETUP_INDENT = 2 * JS_INDENT
|
33
|
+
JS_ENV_SETUP_INDENT: ClassVar[str] = 2 * JS_INDENT
|
27
34
|
#: Template for the jobscript shebang line.
|
28
|
-
JS_SHEBANG = ""
|
29
|
-
#: Template for the
|
30
|
-
|
35
|
+
JS_SHEBANG: ClassVar[str] = ""
|
36
|
+
#: Template for the jobscript functions file.
|
37
|
+
JS_FUNCS: ClassVar[str] = dedent(
|
31
38
|
"""\
|
32
39
|
function {workflow_app_alias} {{
|
33
40
|
& {{
|
34
41
|
{env_setup}{app_invoc} `
|
35
|
-
--with-config log_file_path "$
|
42
|
+
--with-config log_file_path "$env:{app_caps}_LOG_PATH" `
|
36
43
|
--config-dir "{config_dir}" `
|
37
44
|
--config-key "{config_invoc_key}" `
|
38
45
|
$args
|
@@ -42,6 +49,12 @@ class WindowsPowerShell(Shell):
|
|
42
49
|
function get_nth_line($file, $line) {{
|
43
50
|
Get-Content $file | Select-Object -Skip $line -First 1
|
44
51
|
}}
|
52
|
+
"""
|
53
|
+
)
|
54
|
+
#: Template for the common part of the jobscript header.
|
55
|
+
JS_HEADER: ClassVar[str] = dedent(
|
56
|
+
"""\
|
57
|
+
$ErrorActionPreference = 'Stop'
|
45
58
|
|
46
59
|
function JoinMultiPath {{
|
47
60
|
$numArgs = $args.Length
|
@@ -50,103 +63,156 @@ class WindowsPowerShell(Shell):
|
|
50
63
|
$path = Join-Path $path $args[$i]
|
51
64
|
}}
|
52
65
|
return $path
|
53
|
-
}}
|
54
|
-
|
55
|
-
function StartJobHere($block) {{
|
56
|
-
$jobInitBlock = [scriptblock]::Create(@"
|
57
|
-
Function wkflow_app {{ $function:wkflow_app }}
|
58
|
-
Function get_nth_line {{ $function:get_nth_line }}
|
59
|
-
Function JoinMultiPath {{ $function:JoinMultiPath }}
|
60
|
-
Set-Location '$pwd'
|
61
|
-
"@)
|
62
|
-
Start-Job -InitializationScript $jobInitBlock -Script $block
|
63
|
-
}}
|
66
|
+
}}
|
64
67
|
|
65
68
|
$WK_PATH = $(Get-Location)
|
66
69
|
$WK_PATH_ARG = $WK_PATH
|
67
70
|
$SUB_IDX = {sub_idx}
|
68
71
|
$JS_IDX = {js_idx}
|
69
|
-
|
70
|
-
$
|
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
|
71
94
|
"""
|
72
95
|
)
|
73
96
|
#: Template for the jobscript header when directly executed.
|
74
|
-
JS_DIRECT_HEADER = dedent(
|
97
|
+
JS_DIRECT_HEADER: ClassVar[str] = dedent(
|
75
98
|
"""\
|
76
99
|
{shebang}
|
77
|
-
|
78
100
|
{header}
|
79
101
|
{wait_command}
|
80
102
|
"""
|
81
103
|
)
|
82
|
-
#: Template for the
|
83
|
-
|
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(
|
84
120
|
"""\
|
85
|
-
$
|
86
|
-
$
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
$EAR_ID = ($elem_EAR_IDs -split "{EAR_files_delimiter}")[$JS_act_idx]
|
91
|
-
if ($EAR_ID -eq -1) {{
|
92
|
-
continue
|
93
|
-
}}
|
121
|
+
$EAR_ID = ($elem_EAR_IDs -split "{EAR_files_delimiter}")[$block_act_idx]
|
122
|
+
if ($EAR_ID -eq -1) {{
|
123
|
+
continue
|
124
|
+
}}
|
94
125
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
$skip = {workflow_app_alias} internal workflow $WK_PATH get-ear-skipped $EAR_ID 2>> $app_stream_file
|
101
|
-
$exc_sk = $LASTEXITCODE
|
102
|
-
|
103
|
-
if ($exc_sk -eq 0) {{
|
104
|
-
|
105
|
-
if ($skip -eq "1") {{
|
106
|
-
continue
|
107
|
-
}}
|
108
|
-
|
109
|
-
{workflow_app_alias} internal workflow $WK_PATH write-commands $SUB_IDX $JS_IDX $JS_act_idx $EAR_ID 2>&1 >> $app_stream_file
|
110
|
-
$exc_wc = $LASTEXITCODE
|
111
|
-
|
112
|
-
{workflow_app_alias} internal workflow $WK_PATH set-ear-start $EAR_ID 2>&1 >> $app_stream_file
|
113
|
-
$exc_se = $LASTEXITCODE
|
114
|
-
|
115
|
-
if (($exc_wc -eq 0) -and ($exc_se -eq 0)) {{
|
116
|
-
. (Join-Path $run_dir_abs "{commands_file_name}")
|
117
|
-
$exit_code = $LASTEXITCODE
|
118
|
-
}}
|
119
|
-
else {{
|
120
|
-
$exit_code = If ($exc_wc -ne 0) {{$exc_wc}} Else {{$exc_se}}
|
121
|
-
}}
|
122
|
-
}}
|
123
|
-
else {{
|
124
|
-
$exit_code = $exc_sk
|
125
|
-
}}
|
126
|
-
$global:LASTEXITCODE = $null
|
127
|
-
{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
|
128
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}
|
129
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}
|
130
175
|
"""
|
131
176
|
)
|
132
177
|
#: Template for the element processing loop in a jobscript.
|
133
|
-
|
178
|
+
JS_ELEMENT_MULTI_LOOP: ClassVar[str] = dedent(
|
134
179
|
"""\
|
135
|
-
for ($JS_elem_idx =
|
180
|
+
for ($JS_elem_idx = {block_start_elem_idx}; $JS_elem_idx -lt ({block_start_elem_idx} + {num_elements}); $JS_elem_idx += 1) {{
|
136
181
|
{main}
|
137
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
|
+
"""\
|
138
201
|
Set-Location $WK_PATH
|
139
202
|
"""
|
140
203
|
)
|
141
204
|
|
142
|
-
def
|
143
|
-
super().__init__(*args, **kwargs)
|
144
|
-
|
145
|
-
def get_direct_submit_command(self, js_path) -> List[str]:
|
205
|
+
def get_direct_submit_command(self, js_path: str) -> list[str]:
|
146
206
|
"""Get the command for submitting a non-scheduled jobscript."""
|
147
|
-
return self.executable
|
207
|
+
return [*self.executable, "-File", js_path]
|
148
208
|
|
149
|
-
def
|
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:
|
150
216
|
"""Get powershell version information.
|
151
217
|
|
152
218
|
Parameters
|
@@ -157,7 +223,7 @@ class WindowsPowerShell(Shell):
|
|
157
223
|
"""
|
158
224
|
|
159
225
|
proc = subprocess.run(
|
160
|
-
args=self.executable + ["$PSVersionTable.PSVersion.ToString()"],
|
226
|
+
args=self.executable + ["-Command", "$PSVersionTable.PSVersion.ToString()"],
|
161
227
|
stdout=subprocess.PIPE,
|
162
228
|
text=True,
|
163
229
|
)
|
@@ -166,125 +232,117 @@ class WindowsPowerShell(Shell):
|
|
166
232
|
else:
|
167
233
|
raise RuntimeError("Failed to parse PowerShell version information.")
|
168
234
|
|
169
|
-
|
235
|
+
osinfo = {} if exclude_os else get_OS_info_windows()
|
236
|
+
return {
|
170
237
|
"shell_name": "powershell",
|
171
238
|
"shell_executable": self.executable,
|
172
239
|
"shell_version": PS_version,
|
240
|
+
**osinfo,
|
173
241
|
}
|
174
242
|
|
175
|
-
if not exclude_os:
|
176
|
-
out.update(**get_OS_info_windows())
|
177
|
-
|
178
|
-
return out
|
179
|
-
|
180
243
|
@staticmethod
|
181
|
-
def process_app_invoc_executable(app_invoc_exe):
|
244
|
+
def process_app_invoc_executable(app_invoc_exe: str) -> str:
|
182
245
|
if " " in app_invoc_exe:
|
183
246
|
# use call operator and single-quote the executable path:
|
184
247
|
app_invoc_exe = f"& '{app_invoc_exe}'"
|
185
248
|
return app_invoc_exe
|
186
249
|
|
187
|
-
|
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:
|
188
273
|
"""
|
189
274
|
Produce code to assign the output of the command to a shell variable.
|
190
275
|
"""
|
191
276
|
return f"${shell_var_name} = {command}"
|
192
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
|
+
|
326
|
+
@override
|
193
327
|
def format_save_parameter(
|
194
328
|
self,
|
195
329
|
workflow_app_alias: str,
|
196
330
|
param_name: str,
|
197
331
|
shell_var_name: str,
|
198
|
-
EAR_ID: int,
|
199
332
|
cmd_idx: int,
|
200
333
|
stderr: bool,
|
201
|
-
|
334
|
+
app_name: str,
|
335
|
+
) -> str:
|
202
336
|
"""
|
203
337
|
Produce code to save a parameter's value into the workflow persistent store.
|
204
338
|
"""
|
205
339
|
# TODO: quote shell_var_name as well? e.g. if it's a white-space delimited list?
|
206
340
|
# and test.
|
207
341
|
stderr_str = " --stderr" if stderr else ""
|
342
|
+
app_caps = app_name.upper()
|
208
343
|
return (
|
209
|
-
f
|
210
|
-
f
|
211
|
-
f"{param_name} ${shell_var_name} {
|
212
|
-
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}'
|
213
347
|
f"\n"
|
214
348
|
)
|
215
|
-
|
216
|
-
def format_loop_check(self, workflow_app_alias: str, loop_name: str, run_ID: int):
|
217
|
-
"""
|
218
|
-
Produce code to check the looping status of part of a workflow.
|
219
|
-
"""
|
220
|
-
return (
|
221
|
-
f"{workflow_app_alias} "
|
222
|
-
f"internal workflow $WK_PATH check-loop "
|
223
|
-
f"{loop_name} {run_ID} "
|
224
|
-
f"2>&1 >> $app_stream_file"
|
225
|
-
f"\n"
|
226
|
-
)
|
227
|
-
|
228
|
-
def wrap_in_subshell(self, commands: str, abortable: bool) -> str:
|
229
|
-
"""Format commands to run within a child scope.
|
230
|
-
|
231
|
-
This assumes `commands` ends in a newline.
|
232
|
-
|
233
|
-
"""
|
234
|
-
commands = indent(commands, self.JS_INDENT)
|
235
|
-
if abortable:
|
236
|
-
# run commands as a background job, and poll a file to check for abort
|
237
|
-
# requests:
|
238
|
-
return dedent(
|
239
|
-
"""\
|
240
|
-
$job = StartJobHere {{
|
241
|
-
$WK_PATH = $using:WK_PATH
|
242
|
-
$SUB_IDX = $using:SUB_IDX
|
243
|
-
$JS_IDX = $using:JS_IDX
|
244
|
-
$EAR_ID = $using:EAR_ID
|
245
|
-
$app_stream_file= $using:app_stream_file
|
246
|
-
|
247
|
-
{commands}
|
248
|
-
if ($LASTEXITCODE -ne 0) {{
|
249
|
-
throw
|
250
|
-
}}
|
251
|
-
}}
|
252
|
-
|
253
|
-
$is_abort = $null
|
254
|
-
$abort_file = JoinMultiPath $WK_PATH artifacts submissions $SUB_IDX abort_EARs.txt
|
255
|
-
while ($true) {{
|
256
|
-
$is_abort = get_nth_line $abort_file $EAR_ID
|
257
|
-
if ($job.State -ne "Running") {{
|
258
|
-
break
|
259
|
-
}}
|
260
|
-
elseif ($is_abort -eq "1") {{
|
261
|
-
Add-Content -Path $app_stream_file -Value "Abort instruction received; stopping commands..."
|
262
|
-
Stop-Job -Job $job
|
263
|
-
Wait-Job -Job $job
|
264
|
-
break
|
265
|
-
}}
|
266
|
-
else {{
|
267
|
-
Receive-Job -job $job | Write-Output
|
268
|
-
Start-Sleep 1 # TODO: TEMP: increase for production
|
269
|
-
}}
|
270
|
-
}}
|
271
|
-
Receive-Job -job $job | Write-Output
|
272
|
-
if ($job.state -eq "Completed") {{
|
273
|
-
exit 0
|
274
|
-
}}
|
275
|
-
elseif ($is_abort -eq "1") {{
|
276
|
-
exit {abort_exit_code}
|
277
|
-
}}
|
278
|
-
else {{
|
279
|
-
exit 1
|
280
|
-
}}
|
281
|
-
"""
|
282
|
-
).format(commands=commands, abort_exit_code=ABORT_EXIT_CODE)
|
283
|
-
else:
|
284
|
-
# run commands in "foreground":
|
285
|
-
return dedent(
|
286
|
-
"""\
|
287
|
-
& {{
|
288
|
-
{commands}}}
|
289
|
-
"""
|
290
|
-
).format(commands=commands)
|