hpcflow-new2 0.2.0a189__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.
- hpcflow/__pyinstaller/hook-hpcflow.py +8 -6
- hpcflow/_version.py +1 -1
- hpcflow/app.py +1 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
- hpcflow/sdk/__init__.py +21 -15
- hpcflow/sdk/app.py +2133 -770
- hpcflow/sdk/cli.py +281 -250
- hpcflow/sdk/cli_common.py +6 -2
- hpcflow/sdk/config/__init__.py +1 -1
- hpcflow/sdk/config/callbacks.py +77 -42
- hpcflow/sdk/config/cli.py +126 -103
- hpcflow/sdk/config/config.py +578 -311
- hpcflow/sdk/config/config_file.py +131 -95
- hpcflow/sdk/config/errors.py +112 -85
- hpcflow/sdk/config/types.py +145 -0
- hpcflow/sdk/core/actions.py +1054 -994
- hpcflow/sdk/core/app_aware.py +24 -0
- hpcflow/sdk/core/cache.py +81 -63
- hpcflow/sdk/core/command_files.py +275 -185
- hpcflow/sdk/core/commands.py +111 -107
- hpcflow/sdk/core/element.py +724 -503
- hpcflow/sdk/core/enums.py +192 -0
- hpcflow/sdk/core/environment.py +74 -93
- hpcflow/sdk/core/errors.py +398 -51
- hpcflow/sdk/core/json_like.py +540 -272
- hpcflow/sdk/core/loop.py +380 -334
- hpcflow/sdk/core/loop_cache.py +160 -43
- hpcflow/sdk/core/object_list.py +370 -207
- hpcflow/sdk/core/parameters.py +728 -600
- hpcflow/sdk/core/rule.py +59 -41
- hpcflow/sdk/core/run_dir_files.py +33 -22
- hpcflow/sdk/core/task.py +1546 -1325
- hpcflow/sdk/core/task_schema.py +240 -196
- hpcflow/sdk/core/test_utils.py +126 -88
- hpcflow/sdk/core/types.py +387 -0
- hpcflow/sdk/core/utils.py +410 -305
- hpcflow/sdk/core/validation.py +82 -9
- hpcflow/sdk/core/workflow.py +1192 -1028
- hpcflow/sdk/core/zarr_io.py +98 -137
- hpcflow/sdk/demo/cli.py +46 -33
- hpcflow/sdk/helper/cli.py +18 -16
- hpcflow/sdk/helper/helper.py +75 -63
- hpcflow/sdk/helper/watcher.py +61 -28
- hpcflow/sdk/log.py +83 -59
- hpcflow/sdk/persistence/__init__.py +8 -31
- hpcflow/sdk/persistence/base.py +988 -586
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +38 -0
- hpcflow/sdk/persistence/json.py +408 -153
- hpcflow/sdk/persistence/pending.py +158 -123
- hpcflow/sdk/persistence/store_resource.py +37 -22
- hpcflow/sdk/persistence/types.py +307 -0
- hpcflow/sdk/persistence/utils.py +14 -11
- hpcflow/sdk/persistence/zarr.py +477 -420
- hpcflow/sdk/runtime.py +44 -41
- hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
- hpcflow/sdk/submission/jobscript.py +444 -404
- hpcflow/sdk/submission/schedulers/__init__.py +133 -40
- hpcflow/sdk/submission/schedulers/direct.py +97 -71
- hpcflow/sdk/submission/schedulers/sge.py +132 -126
- hpcflow/sdk/submission/schedulers/slurm.py +263 -268
- hpcflow/sdk/submission/schedulers/utils.py +7 -2
- hpcflow/sdk/submission/shells/__init__.py +14 -15
- hpcflow/sdk/submission/shells/base.py +102 -29
- hpcflow/sdk/submission/shells/bash.py +72 -55
- hpcflow/sdk/submission/shells/os_version.py +31 -30
- hpcflow/sdk/submission/shells/powershell.py +37 -29
- hpcflow/sdk/submission/submission.py +203 -257
- hpcflow/sdk/submission/types.py +143 -0
- hpcflow/sdk/typing.py +163 -12
- hpcflow/tests/conftest.py +8 -6
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
- hpcflow/tests/scripts/test_main_scripts.py +60 -30
- hpcflow/tests/shells/wsl/test_wsl_submission.py +6 -4
- hpcflow/tests/unit/test_action.py +86 -75
- hpcflow/tests/unit/test_action_rule.py +9 -4
- hpcflow/tests/unit/test_app.py +13 -6
- hpcflow/tests/unit/test_cli.py +1 -1
- hpcflow/tests/unit/test_command.py +71 -54
- hpcflow/tests/unit/test_config.py +20 -15
- hpcflow/tests/unit/test_config_file.py +21 -18
- hpcflow/tests/unit/test_element.py +58 -62
- hpcflow/tests/unit/test_element_iteration.py +3 -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_json_like.py +44 -35
- hpcflow/tests/unit/test_loop.py +65 -58
- hpcflow/tests/unit/test_object_list.py +17 -12
- hpcflow/tests/unit/test_parameter.py +16 -7
- hpcflow/tests/unit/test_persistence.py +48 -35
- hpcflow/tests/unit/test_resources.py +20 -18
- hpcflow/tests/unit/test_run.py +8 -3
- hpcflow/tests/unit/test_runtime.py +2 -1
- hpcflow/tests/unit/test_schema_input.py +23 -15
- hpcflow/tests/unit/test_shell.py +3 -2
- hpcflow/tests/unit/test_slurm.py +8 -7
- hpcflow/tests/unit/test_submission.py +39 -19
- 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/workflows/test_jobscript.py +2 -1
- hpcflow/tests/workflows/test_workflows.py +18 -13
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a190.dist-info}/METADATA +2 -1
- hpcflow_new2-0.2.0a190.dist-info/RECORD +165 -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.0a190.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a190.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a190.dist-info}/entry_points.txt +0 -0
hpcflow/sdk/log.py
CHANGED
@@ -2,12 +2,40 @@
|
|
2
2
|
Interface to the standard logger, and performance logging utility.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
5
6
|
from functools import wraps
|
6
7
|
import logging
|
8
|
+
import logging.handlers
|
7
9
|
from pathlib import Path
|
8
10
|
import time
|
9
11
|
from collections import defaultdict
|
12
|
+
from collections.abc import Callable, Sequence
|
10
13
|
import statistics
|
14
|
+
from dataclasses import dataclass
|
15
|
+
from typing import ClassVar, TypeVar, TYPE_CHECKING
|
16
|
+
from typing_extensions import ParamSpec
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from .app import BaseApp
|
20
|
+
|
21
|
+
|
22
|
+
P = ParamSpec("P")
|
23
|
+
T = TypeVar("T")
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class _Summary:
|
28
|
+
"""
|
29
|
+
Summary of a particular node's execution time.
|
30
|
+
"""
|
31
|
+
|
32
|
+
number: int
|
33
|
+
mean: float
|
34
|
+
stddev: float
|
35
|
+
min: float
|
36
|
+
max: float
|
37
|
+
sum: float
|
38
|
+
children: dict[tuple[str, ...], _Summary]
|
11
39
|
|
12
40
|
|
13
41
|
class TimeIt:
|
@@ -16,29 +44,28 @@ class TimeIt:
|
|
16
44
|
"""
|
17
45
|
|
18
46
|
#: Whether the instrumentation is active.
|
19
|
-
active = False
|
47
|
+
active: ClassVar = False
|
20
48
|
#: Where to log to.
|
21
|
-
file_path = None
|
49
|
+
file_path: ClassVar[str | None] = None
|
22
50
|
#: The details be tracked.
|
23
|
-
timers = defaultdict(list)
|
51
|
+
timers: ClassVar[dict[tuple[str, ...], list[float]]] = defaultdict(list)
|
24
52
|
#: Traces of the stack.
|
25
|
-
trace = []
|
53
|
+
trace: ClassVar[list[str]] = []
|
26
54
|
#: Trace indices.
|
27
|
-
trace_idx = []
|
55
|
+
trace_idx: ClassVar[list[int]] = []
|
28
56
|
#: Preceding traces.
|
29
|
-
trace_prev = []
|
57
|
+
trace_prev: ClassVar[list[str]] = []
|
30
58
|
#: Preceding trace indices.
|
31
|
-
trace_idx_prev = []
|
59
|
+
trace_idx_prev: ClassVar[list[int]] = []
|
32
60
|
|
33
61
|
@classmethod
|
34
|
-
def decorator(cls, func):
|
62
|
+
def decorator(cls, func: Callable[P, T]) -> Callable[P, T]:
|
35
63
|
"""
|
36
64
|
Decorator for a method that is to have its execution time monitored.
|
37
65
|
"""
|
38
66
|
|
39
67
|
@wraps(func)
|
40
|
-
def wrapper(*args, **kwargs):
|
41
|
-
|
68
|
+
def wrapper(*args, **kwargs) -> T:
|
42
69
|
if not cls.active:
|
43
70
|
return func(*args, **kwargs)
|
44
71
|
|
@@ -68,48 +95,45 @@ class TimeIt:
|
|
68
95
|
return wrapper
|
69
96
|
|
70
97
|
@classmethod
|
71
|
-
def
|
98
|
+
def _summarise(cls) -> dict[tuple[str, ...], _Summary]:
|
72
99
|
"""
|
73
100
|
Produce a machine-readable summary of method execution time statistics.
|
74
101
|
"""
|
75
102
|
stats = {
|
76
|
-
k:
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
103
|
+
k: _Summary(
|
104
|
+
len(v),
|
105
|
+
statistics.mean(v),
|
106
|
+
statistics.pstdev(v),
|
107
|
+
min(v),
|
108
|
+
max(v),
|
109
|
+
sum(v),
|
110
|
+
{},
|
111
|
+
)
|
84
112
|
for k, v in cls.timers.items()
|
85
113
|
}
|
86
114
|
|
87
115
|
# make a graph
|
88
|
-
for
|
89
|
-
stats[k]["children"] = {}
|
90
|
-
|
91
|
-
for key in sorted(stats.keys(), key=lambda x: len(x), reverse=True):
|
116
|
+
for key in sorted(stats, key=lambda x: len(x), reverse=True):
|
92
117
|
if len(key) == 1:
|
93
118
|
continue
|
94
119
|
value = stats.pop(key)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
stats[other_key]["children"][key] = value
|
99
|
-
break
|
120
|
+
parent_key = key[:-1]
|
121
|
+
if parent_key in stats:
|
122
|
+
stats[parent_key].children[key] = value
|
100
123
|
|
101
124
|
return stats
|
102
125
|
|
103
126
|
@classmethod
|
104
|
-
def summarise_string(cls):
|
127
|
+
def summarise_string(cls) -> None:
|
105
128
|
"""
|
106
129
|
Produce a human-readable summary of method execution time statistics.
|
107
130
|
"""
|
108
131
|
|
109
|
-
def _format_nodes(
|
110
|
-
|
111
|
-
|
112
|
-
|
132
|
+
def _format_nodes(
|
133
|
+
node: dict[tuple[str, ...], _Summary],
|
134
|
+
depth: int = 0,
|
135
|
+
depth_final: Sequence[bool] = (),
|
136
|
+
):
|
113
137
|
for idx, (k, v) in enumerate(node.items()):
|
114
138
|
is_final_child = idx == len(node) - 1
|
115
139
|
angle = "└ " if is_final_child else "├ "
|
@@ -117,32 +141,26 @@ class TimeIt:
|
|
117
141
|
if depth > 0:
|
118
142
|
bars = "".join(f"{'│ ' if not i else ' '}" for i in depth_final)
|
119
143
|
k_str = bars + (angle if depth > 0 else "") + f"{k[depth]}"
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
stddev_str = f"({v['stddev']:8.6f})" if number > 1 else f"{f' ':^10s}"
|
144
|
+
min_str = f"{v.min:10.6f}" if v.number > 1 else f"{f'-':^12s}"
|
145
|
+
max_str = f"{v.max:10.6f}" if v.number > 1 else f"{f'-':^12s}"
|
146
|
+
stddev_str = f"({v.stddev:8.6f})" if v.number > 1 else f"{f' ':^10s}"
|
124
147
|
out.append(
|
125
|
-
f"{k_str:.<80s} {v
|
126
|
-
f"{v
|
148
|
+
f"{k_str:.<80s} {v.sum:12.6f} "
|
149
|
+
f"{v.mean:10.6f} {stddev_str} {v.number:8d} "
|
127
150
|
f"{min_str} {max_str} "
|
128
151
|
)
|
129
|
-
depth_final_next = list(depth_final)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
_format_nodes(
|
134
|
-
v["children"], depth=depth + 1, depth_final=depth_final_next
|
135
|
-
)
|
136
|
-
)
|
137
|
-
return out
|
152
|
+
depth_final_next = list(depth_final)
|
153
|
+
if depth > 0:
|
154
|
+
depth_final_next.append(is_final_child)
|
155
|
+
_format_nodes(v.children, depth + 1, depth_final_next)
|
138
156
|
|
139
|
-
summary = cls.
|
157
|
+
summary = cls._summarise()
|
140
158
|
|
141
159
|
out = [
|
142
160
|
f"{'function':^80s} {'sum /s':^12s} {'mean (stddev) /s':^20s} {'N':^8s} "
|
143
161
|
f"{'min /s':^12s} {'max /s':^12s}"
|
144
162
|
]
|
145
|
-
|
163
|
+
_format_nodes(summary)
|
146
164
|
out_str = "\n".join(out)
|
147
165
|
if cls.file_path:
|
148
166
|
Path(cls.file_path).write_text(out_str, encoding="utf-8")
|
@@ -156,22 +174,22 @@ class AppLog:
|
|
156
174
|
"""
|
157
175
|
|
158
176
|
#: Default logging level for the console.
|
159
|
-
DEFAULT_LOG_CONSOLE_LEVEL = "WARNING"
|
177
|
+
DEFAULT_LOG_CONSOLE_LEVEL: ClassVar = "WARNING"
|
160
178
|
#: Default logging level for log files.
|
161
|
-
DEFAULT_LOG_FILE_LEVEL = "INFO"
|
179
|
+
DEFAULT_LOG_FILE_LEVEL: ClassVar = "INFO"
|
162
180
|
|
163
|
-
def __init__(self, app, log_console_level=None):
|
181
|
+
def __init__(self, app: BaseApp, log_console_level: str | None = None) -> None:
|
164
182
|
#: The application context.
|
165
183
|
self.app = app
|
166
184
|
#: The base logger for the application.
|
167
185
|
self.logger = logging.getLogger(app.package_name)
|
168
186
|
self.logger.setLevel(logging.DEBUG)
|
169
187
|
#: The handler for directing logging messages to the console.
|
170
|
-
self.console_handler = self.
|
188
|
+
self.console_handler = self.__add_console_logger(
|
171
189
|
level=log_console_level or AppLog.DEFAULT_LOG_CONSOLE_LEVEL
|
172
190
|
)
|
173
191
|
|
174
|
-
def
|
192
|
+
def __add_console_logger(self, level: str, fmt: str | None = None) -> logging.Handler:
|
175
193
|
fmt = fmt or "%(levelname)s %(name)s: %(message)s"
|
176
194
|
handler = logging.StreamHandler()
|
177
195
|
handler.setFormatter(logging.Formatter(fmt))
|
@@ -179,18 +197,24 @@ class AppLog:
|
|
179
197
|
self.logger.addHandler(handler)
|
180
198
|
return handler
|
181
199
|
|
182
|
-
def update_console_level(self, new_level):
|
200
|
+
def update_console_level(self, new_level: str) -> None:
|
183
201
|
"""
|
184
202
|
Set the logging level for console messages.
|
185
203
|
"""
|
186
204
|
if new_level:
|
187
205
|
self.console_handler.setLevel(new_level.upper())
|
188
206
|
|
189
|
-
def add_file_logger(
|
207
|
+
def add_file_logger(
|
208
|
+
self,
|
209
|
+
path: Path,
|
210
|
+
level: str | None = None,
|
211
|
+
fmt: str | None = None,
|
212
|
+
max_bytes: int | None = None,
|
213
|
+
) -> logging.Handler:
|
190
214
|
"""
|
191
215
|
Add a log file.
|
192
216
|
"""
|
193
|
-
fmt = fmt or
|
217
|
+
fmt = fmt or "%(asctime)s %(levelname)s %(name)s: %(message)s"
|
194
218
|
level = level or AppLog.DEFAULT_LOG_FILE_LEVEL
|
195
219
|
max_bytes = max_bytes or int(10e6)
|
196
220
|
|
@@ -204,7 +228,7 @@ class AppLog:
|
|
204
228
|
self.logger.addHandler(handler)
|
205
229
|
return handler
|
206
230
|
|
207
|
-
def remove_file_handlers(self):
|
231
|
+
def remove_file_handlers(self) -> None:
|
208
232
|
"""Remove all file handlers."""
|
209
233
|
# TODO: store a `file_handlers` attribute as well as `console_handlers`
|
210
234
|
for hdlr in self.logger.handlers:
|
@@ -2,40 +2,17 @@
|
|
2
2
|
Workflow persistence subsystem.
|
3
3
|
"""
|
4
4
|
|
5
|
-
import
|
6
|
-
from
|
7
|
-
import random
|
8
|
-
import string
|
9
|
-
import time
|
10
|
-
from typing import Type, Union
|
5
|
+
from __future__ import annotations
|
6
|
+
from typing import TYPE_CHECKING
|
11
7
|
|
12
|
-
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from .base import PersistentStore
|
13
10
|
|
14
|
-
from hpcflow.sdk.persistence.base import PersistentStore
|
15
|
-
from hpcflow.sdk.persistence.json import JSONPersistentStore
|
16
|
-
from hpcflow.sdk.persistence.zarr import ZarrPersistentStore, ZarrZipPersistentStore
|
17
11
|
|
18
|
-
|
19
|
-
"zarr": ZarrPersistentStore,
|
20
|
-
"zip": ZarrZipPersistentStore,
|
21
|
-
"json": JSONPersistentStore,
|
22
|
-
# "json-single": JSONPersistentStore, # TODO
|
23
|
-
}
|
24
|
-
#: The name of the default persistence store.
|
25
|
-
DEFAULT_STORE_FORMAT = "zarr"
|
26
|
-
#: The persistence formats supported.
|
27
|
-
ALL_STORE_FORMATS = tuple(_ALL_STORE_CLS.keys())
|
28
|
-
#: The persistence formats supported for creation.
|
29
|
-
ALL_CREATE_STORE_FORMATS = tuple(
|
30
|
-
k for k, v in _ALL_STORE_CLS.items() if v._features.create
|
31
|
-
)
|
32
|
-
|
33
|
-
|
34
|
-
def store_cls_from_str(store_format: str) -> Type[PersistentStore]:
|
12
|
+
def store_cls_from_str(store_format: str) -> type[PersistentStore]:
|
35
13
|
"""
|
36
14
|
Get the class that implements the persistence store from its name.
|
37
15
|
"""
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
raise ValueError(f"Store format {store_format!r} not known.")
|
16
|
+
from .discovery import store_cls_from_str as impl
|
17
|
+
|
18
|
+
return impl(store_format)
|