hpcflow-new2 0.2.0a179__py3-none-any.whl → 0.2.0a181__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/_version.py +1 -1
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/sdk/__init__.py +4 -1
- hpcflow/sdk/app.py +160 -15
- hpcflow/sdk/cli.py +14 -0
- hpcflow/sdk/cli_common.py +83 -0
- hpcflow/sdk/config/__init__.py +4 -0
- hpcflow/sdk/config/callbacks.py +25 -2
- hpcflow/sdk/config/cli.py +4 -1
- hpcflow/sdk/config/config.py +188 -14
- hpcflow/sdk/config/config_file.py +91 -3
- hpcflow/sdk/config/errors.py +33 -0
- hpcflow/sdk/core/__init__.py +2 -0
- hpcflow/sdk/core/actions.py +492 -35
- hpcflow/sdk/core/cache.py +22 -0
- hpcflow/sdk/core/command_files.py +221 -5
- hpcflow/sdk/core/commands.py +57 -0
- hpcflow/sdk/core/element.py +407 -8
- hpcflow/sdk/core/environment.py +92 -0
- hpcflow/sdk/core/errors.py +245 -61
- hpcflow/sdk/core/json_like.py +72 -14
- hpcflow/sdk/core/loop.py +122 -21
- hpcflow/sdk/core/loop_cache.py +34 -9
- hpcflow/sdk/core/object_list.py +172 -26
- hpcflow/sdk/core/parallel.py +14 -0
- hpcflow/sdk/core/parameters.py +478 -25
- hpcflow/sdk/core/rule.py +31 -1
- hpcflow/sdk/core/run_dir_files.py +12 -2
- hpcflow/sdk/core/task.py +407 -80
- hpcflow/sdk/core/task_schema.py +70 -9
- hpcflow/sdk/core/test_utils.py +35 -0
- hpcflow/sdk/core/utils.py +101 -4
- hpcflow/sdk/core/validation.py +13 -1
- hpcflow/sdk/core/workflow.py +316 -96
- hpcflow/sdk/core/zarr_io.py +23 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +9 -0
- hpcflow/sdk/helper/helper.py +28 -0
- hpcflow/sdk/helper/watcher.py +33 -0
- hpcflow/sdk/log.py +40 -0
- hpcflow/sdk/persistence/__init__.py +14 -4
- hpcflow/sdk/persistence/base.py +289 -23
- hpcflow/sdk/persistence/json.py +29 -0
- hpcflow/sdk/persistence/pending.py +217 -107
- hpcflow/sdk/persistence/store_resource.py +58 -2
- hpcflow/sdk/persistence/utils.py +8 -0
- hpcflow/sdk/persistence/zarr.py +68 -1
- hpcflow/sdk/runtime.py +52 -10
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/jobscript.py +198 -9
- hpcflow/sdk/submission/jobscript_info.py +13 -0
- hpcflow/sdk/submission/schedulers/__init__.py +60 -0
- hpcflow/sdk/submission/schedulers/direct.py +53 -0
- hpcflow/sdk/submission/schedulers/sge.py +45 -7
- hpcflow/sdk/submission/schedulers/slurm.py +45 -8
- hpcflow/sdk/submission/schedulers/utils.py +4 -0
- hpcflow/sdk/submission/shells/__init__.py +11 -1
- hpcflow/sdk/submission/shells/base.py +32 -1
- hpcflow/sdk/submission/shells/bash.py +36 -1
- hpcflow/sdk/submission/shells/os_version.py +18 -6
- hpcflow/sdk/submission/shells/powershell.py +22 -0
- hpcflow/sdk/submission/submission.py +88 -3
- hpcflow/sdk/typing.py +10 -1
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/METADATA +3 -3
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/RECORD +70 -70
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/entry_points.txt +0 -0
hpcflow/sdk/core/cache.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Dependency resolution cache.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from collections import defaultdict
|
2
6
|
from dataclasses import dataclass
|
3
7
|
from typing import Set, Dict
|
@@ -9,21 +13,39 @@ from hpcflow.sdk.log import TimeIt
|
|
9
13
|
class DependencyCache:
|
10
14
|
"""Class to bulk-retrieve dependencies between elements, iterations, and runs."""
|
11
15
|
|
16
|
+
#: What EARs (by ID) a given EAR depends on.
|
12
17
|
run_dependencies: Dict[int, Set]
|
18
|
+
#: What EARs (by ID) are depending on a given EAR.
|
13
19
|
run_dependents: Dict[int, Set]
|
20
|
+
#: What EARs (by ID) a given iteration depends on.
|
14
21
|
iter_run_dependencies: Dict[int, Set]
|
22
|
+
#: What iterations (by ID) a given iteration depends on.
|
15
23
|
iter_iter_dependencies: Dict[int, Set]
|
24
|
+
#: What iterations (by ID) a given element depends on.
|
16
25
|
elem_iter_dependencies: Dict[int, Set]
|
26
|
+
#: What elements (by ID) a given element depends on.
|
17
27
|
elem_elem_dependencies: Dict[int, Set]
|
28
|
+
#: What elements (by ID) are depending on a given element.
|
18
29
|
elem_elem_dependents: Dict[int, Set]
|
30
|
+
#: Transitive closure of :py:attr:`elem_elem_dependents`.
|
19
31
|
elem_elem_dependents_rec: Dict[int, Set]
|
20
32
|
|
33
|
+
#: The elements of the workflow that this cache was built from.
|
21
34
|
elements: Dict
|
35
|
+
#: The iterations of the workflow that this cache was built from.
|
22
36
|
iterations: Dict
|
23
37
|
|
24
38
|
@classmethod
|
25
39
|
@TimeIt.decorator
|
26
40
|
def build(cls, workflow):
|
41
|
+
"""
|
42
|
+
Build a cache instance.
|
43
|
+
|
44
|
+
Parameters
|
45
|
+
----------
|
46
|
+
workflow: ~hpcflow.app.Workflow
|
47
|
+
The workflow to build the cache from.
|
48
|
+
"""
|
27
49
|
num_iters = workflow.num_element_iterations
|
28
50
|
num_elems = workflow.num_elements
|
29
51
|
num_runs = workflow.num_EARs
|
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Model of files that hold commands.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from __future__ import annotations
|
2
6
|
import copy
|
3
7
|
from dataclasses import dataclass, field
|
@@ -15,12 +19,18 @@ from hpcflow.sdk.core.parameters import _process_demo_data_strings
|
|
15
19
|
|
16
20
|
@dataclass
|
17
21
|
class FileSpec(JSONLike):
|
22
|
+
"""
|
23
|
+
A specification of a file handled by a workflow.
|
24
|
+
"""
|
25
|
+
|
18
26
|
_app_attr = "app"
|
19
27
|
|
20
28
|
_validation_schema = "files_spec_schema.yaml"
|
21
29
|
_child_objects = (ChildObjectSpec(name="name", class_name="FileNameSpec"),)
|
22
30
|
|
31
|
+
#: Label for this file specification.
|
23
32
|
label: str
|
33
|
+
#: The name of the file.
|
24
34
|
name: str
|
25
35
|
_hash_value: Optional[str] = field(default=None, repr=False)
|
26
36
|
|
@@ -30,6 +40,9 @@ class FileSpec(JSONLike):
|
|
30
40
|
)
|
31
41
|
|
32
42
|
def value(self, directory="."):
|
43
|
+
"""
|
44
|
+
The path to a file, optionally resolved with respect to a particular directory.
|
45
|
+
"""
|
33
46
|
return self.name.value(directory)
|
34
47
|
|
35
48
|
def __eq__(self, other: object) -> bool:
|
@@ -41,19 +54,42 @@ class FileSpec(JSONLike):
|
|
41
54
|
|
42
55
|
@property
|
43
56
|
def stem(self):
|
57
|
+
"""
|
58
|
+
The stem of the file name.
|
59
|
+
"""
|
44
60
|
return self.name.stem
|
45
61
|
|
46
62
|
@property
|
47
63
|
def ext(self):
|
64
|
+
"""
|
65
|
+
The extension of the file name.
|
66
|
+
"""
|
48
67
|
return self.name.ext
|
49
68
|
|
50
69
|
|
51
70
|
class FileNameSpec(JSONLike):
|
71
|
+
"""
|
72
|
+
The name of a file handled by a workflow, or a pattern that matches multiple files.
|
73
|
+
|
74
|
+
Parameters
|
75
|
+
----------
|
76
|
+
name: str
|
77
|
+
The name or pattern.
|
78
|
+
args: list
|
79
|
+
Positional arguments to use when formatting the name.
|
80
|
+
Can be omitted if the name does not contain a Python formatting pattern.
|
81
|
+
is_regex: bool
|
82
|
+
If true, the name is used as a regex to search for actual files.
|
83
|
+
"""
|
84
|
+
|
52
85
|
_app_attr = "app"
|
53
86
|
|
54
87
|
def __init__(self, name, args=None, is_regex=False):
|
88
|
+
#: The name or pattern.
|
55
89
|
self.name = name
|
90
|
+
#: Positional arguments to use when formatting the name.
|
56
91
|
self.args = args
|
92
|
+
#: Whether the name is used as a regex to search for actual files.
|
57
93
|
self.is_regex = is_regex
|
58
94
|
|
59
95
|
def __eq__(self, other: object) -> bool:
|
@@ -67,13 +103,28 @@ class FileNameSpec(JSONLike):
|
|
67
103
|
|
68
104
|
@property
|
69
105
|
def stem(self):
|
106
|
+
"""
|
107
|
+
The stem of the name or pattern.
|
108
|
+
"""
|
70
109
|
return self.app.FileNameStem(self)
|
71
110
|
|
72
111
|
@property
|
73
112
|
def ext(self):
|
113
|
+
"""
|
114
|
+
The extension of the name or pattern.
|
115
|
+
"""
|
74
116
|
return self.app.FileNameExt(self)
|
75
117
|
|
76
118
|
def value(self, directory="."):
|
119
|
+
"""
|
120
|
+
Get the template-resolved name of the file
|
121
|
+
(or files matched if the name is a regex pattern).
|
122
|
+
|
123
|
+
Parameters
|
124
|
+
----------
|
125
|
+
directory: str
|
126
|
+
Where to resolve values with respect to.
|
127
|
+
"""
|
77
128
|
format_args = [i.value(directory) for i in self.args or []]
|
78
129
|
value = self.name.format(*format_args)
|
79
130
|
if self.is_regex:
|
@@ -86,22 +137,60 @@ class FileNameSpec(JSONLike):
|
|
86
137
|
|
87
138
|
@dataclass
|
88
139
|
class FileNameStem(JSONLike):
|
140
|
+
"""
|
141
|
+
The stem of a file name.
|
142
|
+
"""
|
143
|
+
|
144
|
+
#: The file specification this is derived from.
|
89
145
|
file_name: app.FileNameSpec
|
90
146
|
|
91
147
|
def value(self, directory=None):
|
148
|
+
"""
|
149
|
+
Get the stem, possibly with directory specified.
|
150
|
+
"""
|
92
151
|
return Path(self.file_name.value(directory)).stem
|
93
152
|
|
94
153
|
|
95
154
|
@dataclass
|
96
155
|
class FileNameExt(JSONLike):
|
156
|
+
"""
|
157
|
+
The extension of a file name.
|
158
|
+
"""
|
159
|
+
|
160
|
+
#: The file specification this is derived from.
|
97
161
|
file_name: app.FileNameSpec
|
98
162
|
|
99
163
|
def value(self, directory=None):
|
164
|
+
"""
|
165
|
+
Get the extension.
|
166
|
+
"""
|
100
167
|
return Path(self.file_name.value(directory)).suffix
|
101
168
|
|
102
169
|
|
103
170
|
@dataclass
|
104
171
|
class InputFileGenerator(JSONLike):
|
172
|
+
"""
|
173
|
+
Represents a script that is run to generate input files for an action.
|
174
|
+
|
175
|
+
Parameters
|
176
|
+
----------
|
177
|
+
input_file:
|
178
|
+
The file to generate.
|
179
|
+
inputs: list[~hpcflow.app.Parameter]
|
180
|
+
The input parameters to the generator.
|
181
|
+
script:
|
182
|
+
The script that generates the input.
|
183
|
+
environment:
|
184
|
+
The environment in which to run the generator.
|
185
|
+
script_pass_env_spec:
|
186
|
+
Whether to pass in the environment.
|
187
|
+
abortable:
|
188
|
+
Whether the generator can be stopped early.
|
189
|
+
Quick-running scripts tend to not need this.
|
190
|
+
rules: list[~hpcflow.app.ActionRule]
|
191
|
+
User-specified rules for whether to run the generator.
|
192
|
+
"""
|
193
|
+
|
105
194
|
_app_attr = "app"
|
106
195
|
|
107
196
|
_child_objects = (
|
@@ -127,12 +216,20 @@ class InputFileGenerator(JSONLike):
|
|
127
216
|
),
|
128
217
|
)
|
129
218
|
|
219
|
+
#: The file to generate.
|
130
220
|
input_file: app.FileSpec
|
221
|
+
#: The input parameters to the generator.
|
131
222
|
inputs: List[app.Parameter]
|
223
|
+
#: The script that generates the inputs.
|
132
224
|
script: str = None
|
225
|
+
#: The environment in which to run the generator.
|
133
226
|
environment: app.Environment = None
|
227
|
+
#: Whether to pass in the environment.
|
134
228
|
script_pass_env_spec: Optional[bool] = False
|
229
|
+
#: Whether the generator can be stopped early.
|
230
|
+
#: Quick-running scripts tend to not need this.
|
135
231
|
abortable: Optional[bool] = False
|
232
|
+
#: User-specified rules for whether to run the generator.
|
136
233
|
rules: Optional[List[app.ActionRule]] = None
|
137
234
|
|
138
235
|
def __post_init__(self):
|
@@ -190,9 +287,10 @@ class InputFileGenerator(JSONLike):
|
|
190
287
|
return out
|
191
288
|
|
192
289
|
def write_source(self, action, env_spec: Dict[str, Any]):
|
193
|
-
|
194
|
-
|
195
|
-
|
290
|
+
"""
|
291
|
+
Write the script if it is specified as a snippet script, otherwise we assume
|
292
|
+
the script already exists in the working directory.
|
293
|
+
"""
|
196
294
|
snip_path = action.get_snippet_script_path(self.script, env_spec)
|
197
295
|
if snip_path:
|
198
296
|
source_str = self.compose_source(snip_path)
|
@@ -203,13 +301,35 @@ class InputFileGenerator(JSONLike):
|
|
203
301
|
@dataclass
|
204
302
|
class OutputFileParser(JSONLike):
|
205
303
|
"""
|
304
|
+
Represents a script that is run to parse output files from an action and create outputs.
|
305
|
+
|
206
306
|
Parameters
|
207
307
|
----------
|
208
|
-
|
308
|
+
output_files: list[FileSpec]
|
309
|
+
The output files that this parser will parse.
|
310
|
+
output: ~hpcflow.app.Parameter
|
209
311
|
The singular output parsed by this parser. Not to be confused with `outputs` (plural).
|
210
|
-
|
312
|
+
script: str
|
313
|
+
The name of the file containing the output file parser source.
|
314
|
+
environment: ~hpcflow.app.Environment
|
315
|
+
The environment to use to run the parser.
|
316
|
+
inputs: list[str]
|
317
|
+
The other inputs to the parser.
|
318
|
+
outputs: list[str]
|
211
319
|
Optional multiple outputs from the upstream actions of the schema that are
|
212
320
|
required to parametrise this parser.
|
321
|
+
options: dict
|
322
|
+
Miscellaneous options.
|
323
|
+
script_pass_env_spec: bool
|
324
|
+
Whether to pass the environment specifier to the script.
|
325
|
+
abortable: bool
|
326
|
+
Whether this script can be aborted.
|
327
|
+
save_files: list[str]
|
328
|
+
The files that should be saved to the persistent store for the workflow.
|
329
|
+
clean_files: list[str]
|
330
|
+
The files that should be immediately removed.
|
331
|
+
rules: list[~hpcflow.app.ActionRule]
|
332
|
+
Rules for whether to enable this parser.
|
213
333
|
"""
|
214
334
|
|
215
335
|
_child_objects = (
|
@@ -249,17 +369,32 @@ class OutputFileParser(JSONLike):
|
|
249
369
|
),
|
250
370
|
)
|
251
371
|
|
372
|
+
#: The output files that this parser will parse.
|
252
373
|
output_files: List[app.FileSpec]
|
374
|
+
#: The singular output parsed by this parser.
|
375
|
+
#: Not to be confused with :py:attr:`outputs` (plural).
|
253
376
|
output: Optional[app.Parameter] = None
|
377
|
+
#: The name of the file containing the output file parser source.
|
254
378
|
script: str = None
|
379
|
+
#: The environment to use to run the parser.
|
255
380
|
environment: Environment = None
|
381
|
+
#: The other inputs to the parser.
|
256
382
|
inputs: List[str] = None
|
383
|
+
#: Optional multiple outputs from the upstream actions of the schema that are
|
384
|
+
#: required to parametrise this parser.
|
385
|
+
#: Not to be confused with :py:attr:`output` (plural).
|
257
386
|
outputs: List[str] = None
|
387
|
+
#: Miscellaneous options.
|
258
388
|
options: Dict = None
|
389
|
+
#: Whether to pass the environment specifier to the script.
|
259
390
|
script_pass_env_spec: Optional[bool] = False
|
391
|
+
#: Whether this script can be aborted.
|
260
392
|
abortable: Optional[bool] = False
|
393
|
+
#: The files that should be saved to the persistent store for the workflow.
|
261
394
|
save_files: Union[List[str], bool] = True
|
395
|
+
#: The files that should be immediately removed.
|
262
396
|
clean_up: Optional[List[str]] = None
|
397
|
+
#: Rules for whether to enable this parser.
|
263
398
|
rules: Optional[List[app.ActionRule]] = None
|
264
399
|
|
265
400
|
def __post_init__(self):
|
@@ -345,6 +480,9 @@ class OutputFileParser(JSONLike):
|
|
345
480
|
return out
|
346
481
|
|
347
482
|
def write_source(self, action, env_spec: Dict[str, Any]):
|
483
|
+
"""
|
484
|
+
Write the actual output parser to a file so it can be enacted.
|
485
|
+
"""
|
348
486
|
if self.output is None:
|
349
487
|
# might be used just for saving files:
|
350
488
|
return
|
@@ -485,20 +623,32 @@ class _FileContentsSpecifier(JSONLike):
|
|
485
623
|
return val
|
486
624
|
|
487
625
|
def read_contents(self):
|
626
|
+
"""
|
627
|
+
Get the actual contents of the file.
|
628
|
+
"""
|
488
629
|
with self.path.open("r") as fh:
|
489
630
|
return fh.read()
|
490
631
|
|
491
632
|
@property
|
492
633
|
def path(self):
|
634
|
+
"""
|
635
|
+
The path to the file.
|
636
|
+
"""
|
493
637
|
path = self._get_value("path")
|
494
638
|
return Path(path) if path else None
|
495
639
|
|
496
640
|
@property
|
497
641
|
def store_contents(self):
|
642
|
+
"""
|
643
|
+
Whether the file's contents are stored in the workflow's persistent store.
|
644
|
+
"""
|
498
645
|
return self._get_value("store_contents")
|
499
646
|
|
500
647
|
@property
|
501
648
|
def contents(self):
|
649
|
+
"""
|
650
|
+
The contents of the file.
|
651
|
+
"""
|
502
652
|
if self.store_contents:
|
503
653
|
contents = self._get_value("contents")
|
504
654
|
else:
|
@@ -508,10 +658,16 @@ class _FileContentsSpecifier(JSONLike):
|
|
508
658
|
|
509
659
|
@property
|
510
660
|
def extension(self):
|
661
|
+
"""
|
662
|
+
The extension of the file.
|
663
|
+
"""
|
511
664
|
return self._get_value("extension")
|
512
665
|
|
513
666
|
@property
|
514
667
|
def workflow(self) -> app.Workflow:
|
668
|
+
"""
|
669
|
+
The owning workflow.
|
670
|
+
"""
|
515
671
|
if self._workflow:
|
516
672
|
return self._workflow
|
517
673
|
elif self._element_set:
|
@@ -519,6 +675,23 @@ class _FileContentsSpecifier(JSONLike):
|
|
519
675
|
|
520
676
|
|
521
677
|
class InputFile(_FileContentsSpecifier):
|
678
|
+
"""
|
679
|
+
An input file.
|
680
|
+
|
681
|
+
Parameters
|
682
|
+
----------
|
683
|
+
file:
|
684
|
+
What file is this?
|
685
|
+
path: Path
|
686
|
+
Where is the (original) file?
|
687
|
+
contents: str
|
688
|
+
What is the contents of the file (if already known)?
|
689
|
+
extension: str
|
690
|
+
What is the extension of the file?
|
691
|
+
store_contents: bool
|
692
|
+
Are the file's contents to be cached in the workflow persistent store?
|
693
|
+
"""
|
694
|
+
|
522
695
|
_child_objects = (
|
523
696
|
ChildObjectSpec(
|
524
697
|
name="file",
|
@@ -536,6 +709,7 @@ class InputFile(_FileContentsSpecifier):
|
|
536
709
|
extension: Optional[str] = "",
|
537
710
|
store_contents: Optional[bool] = True,
|
538
711
|
):
|
712
|
+
#: What file is this?
|
539
713
|
self.file = file
|
540
714
|
if not isinstance(self.file, FileSpec):
|
541
715
|
self.file = self.app.command_files.get(self.file.label)
|
@@ -571,14 +745,39 @@ class InputFile(_FileContentsSpecifier):
|
|
571
745
|
|
572
746
|
@property
|
573
747
|
def normalised_files_path(self):
|
748
|
+
"""
|
749
|
+
Standard name for the file within the workflow.
|
750
|
+
"""
|
574
751
|
return self.file.label
|
575
752
|
|
576
753
|
@property
|
577
754
|
def normalised_path(self):
|
755
|
+
"""
|
756
|
+
Full workflow value path to the file.
|
757
|
+
|
758
|
+
Note
|
759
|
+
----
|
760
|
+
This is not the same as the path in the filesystem.
|
761
|
+
"""
|
578
762
|
return f"input_files.{self.normalised_files_path}"
|
579
763
|
|
580
764
|
|
581
765
|
class InputFileGeneratorSource(_FileContentsSpecifier):
|
766
|
+
"""
|
767
|
+
The source of code for use in an input file generator.
|
768
|
+
|
769
|
+
Parameters
|
770
|
+
----------
|
771
|
+
generator:
|
772
|
+
How to generate the file.
|
773
|
+
path:
|
774
|
+
Path to the file.
|
775
|
+
contents:
|
776
|
+
Contents of the file. Only used when recreating this object.
|
777
|
+
extension:
|
778
|
+
File name extension.
|
779
|
+
"""
|
780
|
+
|
582
781
|
def __init__(
|
583
782
|
self,
|
584
783
|
generator: app.InputFileGenerator,
|
@@ -586,11 +785,27 @@ class InputFileGeneratorSource(_FileContentsSpecifier):
|
|
586
785
|
contents: str = None,
|
587
786
|
extension: str = "",
|
588
787
|
):
|
788
|
+
#: How to generate the file.
|
589
789
|
self.generator = generator
|
590
790
|
super().__init__(path, contents, extension)
|
591
791
|
|
592
792
|
|
593
793
|
class OutputFileParserSource(_FileContentsSpecifier):
|
794
|
+
"""
|
795
|
+
The source of code for use in an output file parser.
|
796
|
+
|
797
|
+
Parameters
|
798
|
+
----------
|
799
|
+
parser:
|
800
|
+
How to parse the file.
|
801
|
+
path: Path
|
802
|
+
Path to the file.
|
803
|
+
contents:
|
804
|
+
Contents of the file. Only used when recreating this object.
|
805
|
+
extension:
|
806
|
+
File name extension.
|
807
|
+
"""
|
808
|
+
|
594
809
|
def __init__(
|
595
810
|
self,
|
596
811
|
parser: app.OutputFileParser,
|
@@ -598,5 +813,6 @@ class OutputFileParserSource(_FileContentsSpecifier):
|
|
598
813
|
contents: str = None,
|
599
814
|
extension: str = "",
|
600
815
|
):
|
816
|
+
#: How to parse the file.
|
601
817
|
self.parser = parser
|
602
818
|
super().__init__(path, contents, extension)
|
hpcflow/sdk/core/commands.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Model of a command run in an action.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from dataclasses import dataclass, field
|
2
6
|
from functools import partial
|
3
7
|
from pathlib import Path
|
@@ -15,6 +19,32 @@ from hpcflow.sdk.core.parameters import ParameterValue
|
|
15
19
|
|
16
20
|
@dataclass
|
17
21
|
class Command(JSONLike):
|
22
|
+
"""
|
23
|
+
A command that may be run within a workflow action.
|
24
|
+
|
25
|
+
Parameters
|
26
|
+
----------
|
27
|
+
command: str
|
28
|
+
The actual command.
|
29
|
+
executable: str
|
30
|
+
The executable to run,
|
31
|
+
from the set of executable managed by the environment.
|
32
|
+
arguments: list[str]
|
33
|
+
The arguments to pass in.
|
34
|
+
variables: dict[str, str]
|
35
|
+
Values that may be substituted when preparing the arguments.
|
36
|
+
stdout: str
|
37
|
+
The name of a file to write standard output to.
|
38
|
+
stderr: str
|
39
|
+
The name of a file to write standard error to.
|
40
|
+
stdin: str
|
41
|
+
The name of a file to read standard input from.
|
42
|
+
rules: list[~hpcflow.app.ActionRule]
|
43
|
+
Rules that state whether this command is eligible to run.
|
44
|
+
"""
|
45
|
+
|
46
|
+
# TODO: What is the difference between command and executable?
|
47
|
+
|
18
48
|
_app_attr = "app"
|
19
49
|
_child_objects = (
|
20
50
|
ChildObjectSpec(
|
@@ -25,13 +55,23 @@ class Command(JSONLike):
|
|
25
55
|
),
|
26
56
|
)
|
27
57
|
|
58
|
+
#: The actual command.
|
59
|
+
#: Overrides :py:attr:`executable`.
|
28
60
|
command: Optional[str] = None
|
61
|
+
#: The executable to run,
|
62
|
+
#: from the set of executable managed by the environment.
|
29
63
|
executable: Optional[str] = None
|
64
|
+
#: The arguments to pass in.
|
30
65
|
arguments: Optional[List[str]] = None
|
66
|
+
#: Values that may be substituted when preparing the arguments.
|
31
67
|
variables: Optional[Dict[str, str]] = None
|
68
|
+
#: The name of a file to write standard output to.
|
32
69
|
stdout: Optional[str] = None
|
70
|
+
#: The name of a file to write standard error to.
|
33
71
|
stderr: Optional[str] = None
|
72
|
+
#: The name of a file to read standard input from.
|
34
73
|
stdin: Optional[str] = None
|
74
|
+
#: Rules that state whether this command is eligible to run.
|
35
75
|
rules: Optional[List[app.ActionRule]] = field(default_factory=lambda: [])
|
36
76
|
|
37
77
|
def __repr__(self) -> str:
|
@@ -207,6 +247,9 @@ class Command(JSONLike):
|
|
207
247
|
return cmd_str, shell_vars
|
208
248
|
|
209
249
|
def get_output_types(self):
|
250
|
+
"""
|
251
|
+
Get whether stdout and stderr are workflow parameters.
|
252
|
+
"""
|
210
253
|
# note: we use "parameter" rather than "output", because it could be a schema
|
211
254
|
# output or schema input.
|
212
255
|
pattern = (
|
@@ -253,6 +296,20 @@ class Command(JSONLike):
|
|
253
296
|
return kwargs
|
254
297
|
|
255
298
|
def process_std_stream(self, name: str, value: str, stderr: bool):
|
299
|
+
"""
|
300
|
+
Process a description of a standard stread from a command to get how it becomes
|
301
|
+
a workflow parameter for later actions.
|
302
|
+
|
303
|
+
Parameters
|
304
|
+
---------
|
305
|
+
name:
|
306
|
+
The name of the output, describing how to process things.
|
307
|
+
value:
|
308
|
+
The actual value read from the stream.
|
309
|
+
stderr:
|
310
|
+
If true, this is handling the stderr stream. If false, the stdout stream.
|
311
|
+
"""
|
312
|
+
|
256
313
|
def _parse_list(lst_str: str, item_type: str = "str", delim: str = " "):
|
257
314
|
return [parse_types[item_type](i) for i in lst_str.split(delim)]
|
258
315
|
|