hpcflow-new2 0.2.0a188__py3-none-any.whl → 0.2.0a190__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 (115) hide show
  1. hpcflow/__pyinstaller/hook-hpcflow.py +8 -6
  2. hpcflow/_version.py +1 -1
  3. hpcflow/app.py +1 -0
  4. hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
  5. hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
  6. hpcflow/sdk/__init__.py +21 -15
  7. hpcflow/sdk/app.py +2133 -770
  8. hpcflow/sdk/cli.py +281 -250
  9. hpcflow/sdk/cli_common.py +6 -2
  10. hpcflow/sdk/config/__init__.py +1 -1
  11. hpcflow/sdk/config/callbacks.py +77 -42
  12. hpcflow/sdk/config/cli.py +126 -103
  13. hpcflow/sdk/config/config.py +578 -311
  14. hpcflow/sdk/config/config_file.py +131 -95
  15. hpcflow/sdk/config/errors.py +112 -85
  16. hpcflow/sdk/config/types.py +145 -0
  17. hpcflow/sdk/core/actions.py +1054 -994
  18. hpcflow/sdk/core/app_aware.py +24 -0
  19. hpcflow/sdk/core/cache.py +81 -63
  20. hpcflow/sdk/core/command_files.py +275 -185
  21. hpcflow/sdk/core/commands.py +111 -107
  22. hpcflow/sdk/core/element.py +724 -503
  23. hpcflow/sdk/core/enums.py +192 -0
  24. hpcflow/sdk/core/environment.py +74 -93
  25. hpcflow/sdk/core/errors.py +398 -51
  26. hpcflow/sdk/core/json_like.py +540 -272
  27. hpcflow/sdk/core/loop.py +380 -334
  28. hpcflow/sdk/core/loop_cache.py +160 -43
  29. hpcflow/sdk/core/object_list.py +370 -207
  30. hpcflow/sdk/core/parameters.py +728 -600
  31. hpcflow/sdk/core/rule.py +59 -41
  32. hpcflow/sdk/core/run_dir_files.py +33 -22
  33. hpcflow/sdk/core/task.py +1546 -1325
  34. hpcflow/sdk/core/task_schema.py +240 -196
  35. hpcflow/sdk/core/test_utils.py +126 -88
  36. hpcflow/sdk/core/types.py +387 -0
  37. hpcflow/sdk/core/utils.py +410 -305
  38. hpcflow/sdk/core/validation.py +82 -9
  39. hpcflow/sdk/core/workflow.py +1192 -1028
  40. hpcflow/sdk/core/zarr_io.py +98 -137
  41. hpcflow/sdk/demo/cli.py +46 -33
  42. hpcflow/sdk/helper/cli.py +18 -16
  43. hpcflow/sdk/helper/helper.py +75 -63
  44. hpcflow/sdk/helper/watcher.py +61 -28
  45. hpcflow/sdk/log.py +83 -59
  46. hpcflow/sdk/persistence/__init__.py +8 -31
  47. hpcflow/sdk/persistence/base.py +988 -586
  48. hpcflow/sdk/persistence/defaults.py +6 -0
  49. hpcflow/sdk/persistence/discovery.py +38 -0
  50. hpcflow/sdk/persistence/json.py +408 -153
  51. hpcflow/sdk/persistence/pending.py +158 -123
  52. hpcflow/sdk/persistence/store_resource.py +37 -22
  53. hpcflow/sdk/persistence/types.py +307 -0
  54. hpcflow/sdk/persistence/utils.py +14 -11
  55. hpcflow/sdk/persistence/zarr.py +477 -420
  56. hpcflow/sdk/runtime.py +44 -41
  57. hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
  58. hpcflow/sdk/submission/jobscript.py +444 -404
  59. hpcflow/sdk/submission/schedulers/__init__.py +133 -40
  60. hpcflow/sdk/submission/schedulers/direct.py +97 -71
  61. hpcflow/sdk/submission/schedulers/sge.py +132 -126
  62. hpcflow/sdk/submission/schedulers/slurm.py +263 -268
  63. hpcflow/sdk/submission/schedulers/utils.py +7 -2
  64. hpcflow/sdk/submission/shells/__init__.py +14 -15
  65. hpcflow/sdk/submission/shells/base.py +102 -29
  66. hpcflow/sdk/submission/shells/bash.py +72 -55
  67. hpcflow/sdk/submission/shells/os_version.py +31 -30
  68. hpcflow/sdk/submission/shells/powershell.py +37 -29
  69. hpcflow/sdk/submission/submission.py +203 -257
  70. hpcflow/sdk/submission/types.py +143 -0
  71. hpcflow/sdk/typing.py +163 -12
  72. hpcflow/tests/conftest.py +8 -6
  73. hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
  74. hpcflow/tests/scripts/test_main_scripts.py +60 -30
  75. hpcflow/tests/shells/wsl/test_wsl_submission.py +6 -4
  76. hpcflow/tests/unit/test_action.py +86 -75
  77. hpcflow/tests/unit/test_action_rule.py +9 -4
  78. hpcflow/tests/unit/test_app.py +13 -6
  79. hpcflow/tests/unit/test_cli.py +1 -1
  80. hpcflow/tests/unit/test_command.py +71 -54
  81. hpcflow/tests/unit/test_config.py +20 -15
  82. hpcflow/tests/unit/test_config_file.py +21 -18
  83. hpcflow/tests/unit/test_element.py +58 -62
  84. hpcflow/tests/unit/test_element_iteration.py +3 -1
  85. hpcflow/tests/unit/test_element_set.py +29 -19
  86. hpcflow/tests/unit/test_group.py +4 -2
  87. hpcflow/tests/unit/test_input_source.py +116 -93
  88. hpcflow/tests/unit/test_input_value.py +29 -24
  89. hpcflow/tests/unit/test_json_like.py +44 -35
  90. hpcflow/tests/unit/test_loop.py +65 -58
  91. hpcflow/tests/unit/test_object_list.py +17 -12
  92. hpcflow/tests/unit/test_parameter.py +16 -7
  93. hpcflow/tests/unit/test_persistence.py +48 -35
  94. hpcflow/tests/unit/test_resources.py +20 -18
  95. hpcflow/tests/unit/test_run.py +8 -3
  96. hpcflow/tests/unit/test_runtime.py +2 -1
  97. hpcflow/tests/unit/test_schema_input.py +23 -15
  98. hpcflow/tests/unit/test_shell.py +3 -2
  99. hpcflow/tests/unit/test_slurm.py +8 -7
  100. hpcflow/tests/unit/test_submission.py +39 -19
  101. hpcflow/tests/unit/test_task.py +352 -247
  102. hpcflow/tests/unit/test_task_schema.py +33 -20
  103. hpcflow/tests/unit/test_utils.py +9 -11
  104. hpcflow/tests/unit/test_value_sequence.py +15 -12
  105. hpcflow/tests/unit/test_workflow.py +114 -83
  106. hpcflow/tests/unit/test_workflow_template.py +0 -1
  107. hpcflow/tests/workflows/test_jobscript.py +2 -1
  108. hpcflow/tests/workflows/test_workflows.py +18 -13
  109. {hpcflow_new2-0.2.0a188.dist-info → hpcflow_new2-0.2.0a190.dist-info}/METADATA +2 -1
  110. hpcflow_new2-0.2.0a190.dist-info/RECORD +165 -0
  111. hpcflow/sdk/core/parallel.py +0 -21
  112. hpcflow_new2-0.2.0a188.dist-info/RECORD +0 -158
  113. {hpcflow_new2-0.2.0a188.dist-info → hpcflow_new2-0.2.0a190.dist-info}/LICENSE +0 -0
  114. {hpcflow_new2-0.2.0a188.dist-info → hpcflow_new2-0.2.0a190.dist-info}/WHEEL +0 -0
  115. {hpcflow_new2-0.2.0a188.dist-info → hpcflow_new2-0.2.0a190.dist-info}/entry_points.txt +0 -0
@@ -2,16 +2,17 @@
2
2
  Operating system information discovery helpers.
3
3
  """
4
4
 
5
- import os
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 Dict, List, Optional
10
+ from typing import Final
10
11
 
11
- DEFAULT_LINUX_RELEASE_FILE = "/etc/os-release"
12
+ _DEFAULT_LINUX_RELEASE_FILE: Final = "/etc/os-release"
12
13
 
13
14
 
14
- def get_OS_info() -> Dict:
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() -> Dict:
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: Optional[List[str]] = None,
35
- use_py: Optional[bool] = True,
36
- linux_release_file: Optional[str] = None,
37
- ) -> Dict:
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(command):
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 != 0 or exc:
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
- else:
72
- return proc.stdout
72
+ return proc.stdout
73
73
 
74
- WSL_executable = WSL_executable or []
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(WSL_executable + ["uname", "-s"]).strip()
81
- OS_release = try_subprocess_call(WSL_executable + ["uname", "-r"]).strip()
82
- OS_version = try_subprocess_call(WSL_executable + ["uname", "-v"]).strip()
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 DEFAULT_LINUX_RELEASE_FILE
91
- release_out = try_subprocess_call(WSL_executable + ["cat", linux_release_file])
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 = re.search(r"^NAME=\"(.*)\"", release_out, flags=re.MULTILINE)
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 = re.search(r"^VERSION=\"(.*)\"", release_out, flags=re.MULTILINE)
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,32 +2,40 @@
2
2
  Shell models based on Microsoft PowerShell.
3
3
  """
4
4
 
5
+ from __future__ import annotations
5
6
  import subprocess
6
7
  from textwrap import dedent, indent
7
- from typing import Dict, List, Optional
8
+ from typing import TYPE_CHECKING
9
+ from typing_extensions import override
10
+ from hpcflow.sdk.typing import hydrate
8
11
  from hpcflow.sdk.core import ABORT_EXIT_CODE
9
- from hpcflow.sdk.submission.shells import Shell
12
+ from hpcflow.sdk.submission.shells.base import Shell
10
13
  from hpcflow.sdk.submission.shells.os_version import get_OS_info_windows
11
14
 
15
+ if TYPE_CHECKING:
16
+ from typing import ClassVar
17
+ from .base import VersionInfo
12
18
 
19
+
20
+ @hydrate
13
21
  class WindowsPowerShell(Shell):
14
22
  """Class to represent using PowerShell on Windows to generate and submit a jobscript."""
15
23
 
16
24
  # TODO: add snippets that can be used in demo task schemas?
17
25
 
18
26
  #: Default for executable name.
19
- DEFAULT_EXE = "powershell.exe"
27
+ DEFAULT_EXE: ClassVar[str] = "powershell.exe"
20
28
 
21
29
  #: File extension for jobscripts.
22
- JS_EXT = ".ps1"
30
+ JS_EXT: ClassVar[str] = ".ps1"
23
31
  #: Basic indent.
24
- JS_INDENT = " "
32
+ JS_INDENT: ClassVar[str] = " "
25
33
  #: Indent for environment setup.
26
- JS_ENV_SETUP_INDENT = 2 * JS_INDENT
34
+ JS_ENV_SETUP_INDENT: ClassVar[str] = 2 * JS_INDENT
27
35
  #: Template for the jobscript shebang line.
28
- JS_SHEBANG = ""
36
+ JS_SHEBANG: ClassVar[str] = ""
29
37
  #: Template for the common part of the jobscript header.
30
- JS_HEADER = dedent(
38
+ JS_HEADER: ClassVar[str] = dedent(
31
39
  """\
32
40
  function {workflow_app_alias} {{
33
41
  & {{
@@ -71,7 +79,7 @@ class WindowsPowerShell(Shell):
71
79
  """
72
80
  )
73
81
  #: Template for the jobscript header when directly executed.
74
- JS_DIRECT_HEADER = dedent(
82
+ JS_DIRECT_HEADER: ClassVar[str] = dedent(
75
83
  """\
76
84
  {shebang}
77
85
 
@@ -80,7 +88,7 @@ class WindowsPowerShell(Shell):
80
88
  """
81
89
  )
82
90
  #: Template for the jobscript body.
83
- JS_MAIN = dedent(
91
+ JS_MAIN: ClassVar[str] = dedent(
84
92
  """\
85
93
  $elem_EAR_IDs = get_nth_line $EAR_ID_FILE $JS_elem_idx
86
94
  $elem_run_dirs = get_nth_line $ELEM_RUN_DIR_FILE $JS_elem_idx
@@ -101,7 +109,6 @@ class WindowsPowerShell(Shell):
101
109
  $exc_sk = $LASTEXITCODE
102
110
 
103
111
  if ($exc_sk -eq 0) {{
104
-
105
112
  if ($skip -eq "1") {{
106
113
  continue
107
114
  }}
@@ -120,7 +127,7 @@ class WindowsPowerShell(Shell):
120
127
  $exit_code = If ($exc_wc -ne 0) {{$exc_wc}} Else {{$exc_se}}
121
128
  }}
122
129
  }}
123
- else {{
130
+ else {{
124
131
  $exit_code = $exc_sk
125
132
  }}
126
133
  $global:LASTEXITCODE = $null
@@ -130,7 +137,7 @@ class WindowsPowerShell(Shell):
130
137
  """
131
138
  )
132
139
  #: Template for the element processing loop in a jobscript.
133
- JS_ELEMENT_LOOP = dedent(
140
+ JS_ELEMENT_LOOP: ClassVar[str] = dedent(
134
141
  """\
135
142
  for ($JS_elem_idx = 0; $JS_elem_idx -lt {num_elements}; $JS_elem_idx += 1) {{
136
143
  {main}
@@ -139,14 +146,12 @@ class WindowsPowerShell(Shell):
139
146
  """
140
147
  )
141
148
 
142
- def __init__(self, *args, **kwargs):
143
- super().__init__(*args, **kwargs)
144
-
145
- def get_direct_submit_command(self, js_path) -> List[str]:
149
+ def get_direct_submit_command(self, js_path: str) -> list[str]:
146
150
  """Get the command for submitting a non-scheduled jobscript."""
147
- return self.executable + ["-File", js_path]
151
+ return [*self.executable, "-File", js_path]
148
152
 
149
- def get_version_info(self, exclude_os: Optional[bool] = False) -> Dict:
153
+ @override
154
+ def get_version_info(self, exclude_os: bool = False) -> VersionInfo:
150
155
  """Get powershell version information.
151
156
 
152
157
  Parameters
@@ -166,30 +171,29 @@ class WindowsPowerShell(Shell):
166
171
  else:
167
172
  raise RuntimeError("Failed to parse PowerShell version information.")
168
173
 
169
- out = {
174
+ osinfo = {} if exclude_os else get_OS_info_windows()
175
+ return {
170
176
  "shell_name": "powershell",
171
177
  "shell_executable": self.executable,
172
178
  "shell_version": PS_version,
179
+ **osinfo,
173
180
  }
174
181
 
175
- if not exclude_os:
176
- out.update(**get_OS_info_windows())
177
-
178
- return out
179
-
180
182
  @staticmethod
181
- def process_app_invoc_executable(app_invoc_exe):
183
+ def process_app_invoc_executable(app_invoc_exe: str) -> str:
182
184
  if " " in app_invoc_exe:
183
185
  # use call operator and single-quote the executable path:
184
186
  app_invoc_exe = f"& '{app_invoc_exe}'"
185
187
  return app_invoc_exe
186
188
 
187
- def format_stream_assignment(self, shell_var_name, command):
189
+ @override
190
+ def format_stream_assignment(self, shell_var_name: str, command: str) -> str:
188
191
  """
189
192
  Produce code to assign the output of the command to a shell variable.
190
193
  """
191
194
  return f"${shell_var_name} = {command}"
192
195
 
196
+ @override
193
197
  def format_save_parameter(
194
198
  self,
195
199
  workflow_app_alias: str,
@@ -198,7 +202,7 @@ class WindowsPowerShell(Shell):
198
202
  EAR_ID: int,
199
203
  cmd_idx: int,
200
204
  stderr: bool,
201
- ):
205
+ ) -> str:
202
206
  """
203
207
  Produce code to save a parameter's value into the workflow persistent store.
204
208
  """
@@ -213,7 +217,10 @@ class WindowsPowerShell(Shell):
213
217
  f"\n"
214
218
  )
215
219
 
216
- def format_loop_check(self, workflow_app_alias: str, loop_name: str, run_ID: int):
220
+ @override
221
+ def format_loop_check(
222
+ self, workflow_app_alias: str, loop_name: str, run_ID: int
223
+ ) -> str:
217
224
  """
218
225
  Produce code to check the looping status of part of a workflow.
219
226
  """
@@ -225,6 +232,7 @@ class WindowsPowerShell(Shell):
225
232
  f"\n"
226
233
  )
227
234
 
235
+ @override
228
236
  def wrap_in_subshell(self, commands: str, abortable: bool) -> str:
229
237
  """Format commands to run within a child scope.
230
238