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
@@ -0,0 +1,24 @@
1
+ """
2
+ Utility base class for making classes aware of the overall application context.
3
+ """
4
+ from __future__ import annotations
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import ClassVar
9
+ from ..app import BaseApp
10
+
11
+
12
+ class AppAware:
13
+ """
14
+ A base class that marks its subclasses as aware of the application.
15
+
16
+ Attributes
17
+ ----------
18
+ _app: BaseApp
19
+ A class attribute that holds the application instance.
20
+ """
21
+
22
+ __slots__: ClassVar[tuple[str, ...]] = ()
23
+ _app: ClassVar[BaseApp]
24
+ _app_attr: ClassVar[str] = "_app"
hpcflow/sdk/core/cache.py CHANGED
@@ -2,42 +2,53 @@
2
2
  Dependency resolution cache.
3
3
  """
4
4
 
5
+ from __future__ import annotations
5
6
  from collections import defaultdict
6
7
  from dataclasses import dataclass
7
- from typing import Set, Dict
8
+ from itertools import chain
9
+ from typing import TYPE_CHECKING
8
10
 
9
11
  from hpcflow.sdk.log import TimeIt
10
12
 
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Sequence
15
+ from typing_extensions import Self
16
+ from .element import Element, ElementIteration
17
+ from .workflow import Workflow
18
+ from ..persistence.base import StoreEAR, StoreElement, StoreElementIter
19
+
11
20
 
12
21
  @dataclass
13
22
  class DependencyCache:
14
- """Class to bulk-retrieve dependencies between elements, iterations, and runs."""
23
+ """
24
+ Class to bulk-retrieve dependencies between elements, iterations, and runs.
25
+ """
15
26
 
16
27
  #: What EARs (by ID) a given EAR depends on.
17
- run_dependencies: Dict[int, Set]
28
+ run_dependencies: dict[int, set[int]]
18
29
  #: What EARs (by ID) are depending on a given EAR.
19
- run_dependents: Dict[int, Set]
30
+ run_dependents: dict[int, set[int]]
20
31
  #: What EARs (by ID) a given iteration depends on.
21
- iter_run_dependencies: Dict[int, Set]
32
+ iter_run_dependencies: dict[int, set[int]]
22
33
  #: What iterations (by ID) a given iteration depends on.
23
- iter_iter_dependencies: Dict[int, Set]
34
+ iter_iter_dependencies: dict[int, set[int]]
24
35
  #: What iterations (by ID) a given element depends on.
25
- elem_iter_dependencies: Dict[int, Set]
36
+ elem_iter_dependencies: dict[int, set[int]]
26
37
  #: What elements (by ID) a given element depends on.
27
- elem_elem_dependencies: Dict[int, Set]
38
+ elem_elem_dependencies: dict[int, set[int]]
28
39
  #: What elements (by ID) are depending on a given element.
29
- elem_elem_dependents: Dict[int, Set]
40
+ elem_elem_dependents: dict[int, set[int]]
30
41
  #: Transitive closure of :py:attr:`elem_elem_dependents`.
31
- elem_elem_dependents_rec: Dict[int, Set]
42
+ elem_elem_dependents_rec: dict[int, set[int]]
32
43
 
33
44
  #: The elements of the workflow that this cache was built from.
34
- elements: Dict
45
+ elements: list[Element]
35
46
  #: The iterations of the workflow that this cache was built from.
36
- iterations: Dict
47
+ iterations: list[ElementIteration]
37
48
 
38
49
  @classmethod
39
50
  @TimeIt.decorator
40
- def build(cls, workflow):
51
+ def build(cls, workflow: Workflow) -> Self:
41
52
  """
42
53
  Build a cache instance.
43
54
 
@@ -50,54 +61,59 @@ class DependencyCache:
50
61
  num_elems = workflow.num_elements
51
62
  num_runs = workflow.num_EARs
52
63
 
53
- all_store_runs = workflow._store.get_EARs(list(range(num_runs)))
54
- all_store_iters = workflow._store.get_element_iterations(list(range(num_iters)))
55
- all_store_elements = workflow._store.get_elements(list(range(num_elems)))
64
+ all_store_runs: Sequence[StoreEAR] = workflow._store.get_EARs(range(num_runs))
65
+ all_store_iters: Sequence[
66
+ StoreElementIter
67
+ ] = workflow._store.get_element_iterations(range(num_iters))
68
+ all_store_elements: Sequence[StoreElement] = workflow._store.get_elements(
69
+ range(num_elems)
70
+ )
56
71
  all_param_sources = workflow.get_all_parameter_sources()
57
- all_data_idx = [
72
+ all_data_idx = (
58
73
  {
59
74
  k: v if isinstance(v, list) else [v]
60
- for k, v in i.data_idx.items()
61
- if k not in ("repeats.",)
75
+ for k, v in store_ear.data_idx.items()
76
+ if not k.startswith("repeats.")
62
77
  }
63
- for i in all_store_runs
64
- ]
78
+ for store_ear in all_store_runs
79
+ )
65
80
 
66
81
  # run dependencies and dependents
67
- run_dependencies = {}
68
- run_dependents = defaultdict(set)
69
- for idx, i in enumerate(all_data_idx):
70
- run_i_sources = set()
71
- for j in i.values():
72
- for k in j:
73
- run_k = all_param_sources[k].get("EAR_ID")
74
- if run_k is not None and run_k != idx:
75
- run_i_sources.add(run_k)
82
+ run_dependencies: dict[int, set[int]] = {}
83
+ run_dependents: defaultdict[int, set[int]] = defaultdict(set)
84
+ for idx, dict_i in enumerate(all_data_idx):
85
+ run_i_sources = set(
86
+ run_k
87
+ for idx in chain.from_iterable(dict_i.values())
88
+ if (run_k := all_param_sources[idx].get("EAR_ID")) is not None
89
+ and run_k != idx
90
+ )
76
91
  run_dependencies[idx] = run_i_sources
77
92
  for m in run_i_sources:
78
93
  run_dependents[m].add(idx)
79
94
 
80
- # add missing:
81
- for k in range(num_runs):
82
- run_dependents[k]
83
-
84
- run_dependents = dict(run_dependents)
95
+ # add missing and downgrade to dict:
96
+ for run_idx in range(num_runs):
97
+ run_dependents[run_idx]
98
+ run_dependents.default_factory = None
85
99
 
86
100
  # iteration dependencies
87
101
  all_iter_run_IDs = {
88
- i.id_: [k for j in i.EAR_IDs.values() for k in j] for i in all_store_iters
102
+ iter_.id_: tuple(chain.from_iterable((iter_.EAR_IDs or {}).values()))
103
+ for iter_ in all_store_iters
89
104
  }
90
105
  # for each iteration, which runs does it depend on?
91
106
  iter_run_dependencies = {
92
- k: set(j for i in v for j in run_dependencies[i])
107
+ k: set(j for idx in v for j in run_dependencies[idx])
93
108
  for k, v in all_iter_run_IDs.items()
94
109
  }
95
110
 
96
111
  # for each run, which iteration does it belong to?
97
- all_run_iter_IDs = {}
98
- for iter_ID, run_IDs in all_iter_run_IDs.items():
99
- for run_ID in run_IDs:
100
- all_run_iter_IDs[run_ID] = iter_ID
112
+ all_run_iter_IDs = {
113
+ run_ID: iter_ID
114
+ for iter_ID, run_IDs in all_iter_run_IDs.items()
115
+ for run_ID in run_IDs
116
+ }
101
117
 
102
118
  # for each iteration, which iterations does it depend on?
103
119
  iter_iter_dependencies = {
@@ -105,47 +121,49 @@ class DependencyCache:
105
121
  for k, v in iter_run_dependencies.items()
106
122
  }
107
123
 
108
- all_elem_iter_IDs = {i.id_: i.iteration_IDs for i in all_store_elements}
124
+ all_elem_iter_IDs = {el.id_: el.iteration_IDs for el in all_store_elements}
109
125
 
110
126
  elem_iter_dependencies = {
111
- k: set(j for i in v for j in iter_iter_dependencies[i])
112
- for k, v in all_elem_iter_IDs.items()
127
+ elem_ID: set(j for i in iter_IDs for j in iter_iter_dependencies[i])
128
+ for elem_ID, iter_IDs in all_elem_iter_IDs.items()
113
129
  }
114
130
 
115
131
  # for each iteration, which element does it belong to?
116
- all_iter_elem_IDs = {}
117
- for elem_ID, iter_IDs in all_elem_iter_IDs.items():
118
- for iter_ID in iter_IDs:
119
- all_iter_elem_IDs[iter_ID] = elem_ID
132
+ all_iter_elem_IDs = {
133
+ iter_ID: elem_ID
134
+ for elem_ID, iter_IDs in all_elem_iter_IDs.items()
135
+ for iter_ID in iter_IDs
136
+ }
120
137
 
121
138
  # element dependencies
122
139
  elem_elem_dependencies = {
123
- k: set(all_iter_elem_IDs[i] for i in v)
124
- for k, v in elem_iter_dependencies.items()
140
+ k: set(all_iter_elem_IDs[i] for i in dep_set)
141
+ for k, dep_set in elem_iter_dependencies.items()
125
142
  }
126
143
 
127
144
  # for each element, which elements depend on it (directly)?
128
- elem_elem_dependents = defaultdict(set)
129
- for k, v in elem_elem_dependencies.items():
130
- for i in v:
145
+ elem_elem_dependents: defaultdict[int, set[int]] = defaultdict(set)
146
+ for k, dep_set in elem_elem_dependencies.items():
147
+ for i in dep_set:
131
148
  elem_elem_dependents[i].add(k)
132
149
 
133
150
  # for each element, which elements depend on it (recursively)?
134
- elem_elem_dependents_rec = defaultdict(set)
135
- for k in list(elem_elem_dependents):
151
+ elem_elem_dependents_rec: defaultdict[int, set[int]] = defaultdict(set)
152
+ for k in tuple(elem_elem_dependents):
153
+ # NB: code below modifies elem_elem_dependents during this loop;
154
+ # copy above is mandatory!
136
155
  for i in elem_elem_dependents[k]:
137
156
  elem_elem_dependents_rec[k].add(i)
138
157
  elem_elem_dependents_rec[k].update(
139
- {m for m in elem_elem_dependents[i] if m != k}
158
+ m for m in elem_elem_dependents[i] if m != k
140
159
  )
141
160
 
142
- # add missing keys:
143
- for k in range(num_elems):
144
- elem_elem_dependents[k]
145
- elem_elem_dependents_rec[k]
146
-
147
- elem_elem_dependents = dict(elem_elem_dependents)
148
- elem_elem_dependents_rec = dict(elem_elem_dependents_rec)
161
+ # add missing keys and downgrade to dict:
162
+ for elem_idx in range(num_elems):
163
+ elem_elem_dependents[elem_idx]
164
+ elem_elem_dependents_rec[elem_idx]
165
+ elem_elem_dependents.default_factory = None
166
+ elem_elem_dependents_rec.default_factory = None
149
167
 
150
168
  elements = workflow.get_all_elements()
151
169
  iterations = workflow.get_all_element_iterations()