hpcflow 0.1.9__py3-none-any.whl → 0.2.0a271__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 (275) hide show
  1. hpcflow/__init__.py +2 -11
  2. hpcflow/__pyinstaller/__init__.py +5 -0
  3. hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
  4. hpcflow/_version.py +1 -1
  5. hpcflow/app.py +43 -0
  6. hpcflow/cli.py +2 -462
  7. hpcflow/data/demo_data_manifest/__init__.py +3 -0
  8. hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
  9. hpcflow/data/jinja_templates/test/test_template.txt +8 -0
  10. hpcflow/data/programs/hello_world/README.md +1 -0
  11. hpcflow/data/programs/hello_world/hello_world.c +87 -0
  12. hpcflow/data/programs/hello_world/linux/hello_world +0 -0
  13. hpcflow/data/programs/hello_world/macos/hello_world +0 -0
  14. hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
  15. hpcflow/data/scripts/__init__.py +1 -0
  16. hpcflow/data/scripts/bad_script.py +2 -0
  17. hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
  18. hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
  19. hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
  20. hpcflow/data/scripts/do_nothing.py +2 -0
  21. hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
  22. hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
  23. hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
  24. hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
  25. hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
  26. hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
  27. hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
  28. hpcflow/data/scripts/generate_t1_file_01.py +7 -0
  29. hpcflow/data/scripts/import_future_script.py +7 -0
  30. hpcflow/data/scripts/input_file_generator_basic.py +3 -0
  31. hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
  32. hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
  33. hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
  34. hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
  35. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
  36. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
  37. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
  38. hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
  39. hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
  40. hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
  41. hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
  42. hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
  43. hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
  44. hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
  45. hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
  46. hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
  47. hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
  48. hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
  49. hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
  50. hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
  51. hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
  52. hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
  53. hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
  54. hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
  55. hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
  56. hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
  57. hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
  58. hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
  59. hpcflow/data/scripts/output_file_parser_basic.py +3 -0
  60. hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
  61. hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
  62. hpcflow/data/scripts/parse_t1_file_01.py +4 -0
  63. hpcflow/data/scripts/script_exit_test.py +5 -0
  64. hpcflow/data/template_components/__init__.py +1 -0
  65. hpcflow/data/template_components/command_files.yaml +26 -0
  66. hpcflow/data/template_components/environments.yaml +13 -0
  67. hpcflow/data/template_components/parameters.yaml +14 -0
  68. hpcflow/data/template_components/task_schemas.yaml +139 -0
  69. hpcflow/data/workflows/workflow_1.yaml +5 -0
  70. hpcflow/examples.ipynb +1037 -0
  71. hpcflow/sdk/__init__.py +149 -0
  72. hpcflow/sdk/app.py +4266 -0
  73. hpcflow/sdk/cli.py +1479 -0
  74. hpcflow/sdk/cli_common.py +385 -0
  75. hpcflow/sdk/config/__init__.py +5 -0
  76. hpcflow/sdk/config/callbacks.py +246 -0
  77. hpcflow/sdk/config/cli.py +388 -0
  78. hpcflow/sdk/config/config.py +1410 -0
  79. hpcflow/sdk/config/config_file.py +501 -0
  80. hpcflow/sdk/config/errors.py +272 -0
  81. hpcflow/sdk/config/types.py +150 -0
  82. hpcflow/sdk/core/__init__.py +38 -0
  83. hpcflow/sdk/core/actions.py +3857 -0
  84. hpcflow/sdk/core/app_aware.py +25 -0
  85. hpcflow/sdk/core/cache.py +224 -0
  86. hpcflow/sdk/core/command_files.py +814 -0
  87. hpcflow/sdk/core/commands.py +424 -0
  88. hpcflow/sdk/core/element.py +2071 -0
  89. hpcflow/sdk/core/enums.py +221 -0
  90. hpcflow/sdk/core/environment.py +256 -0
  91. hpcflow/sdk/core/errors.py +1043 -0
  92. hpcflow/sdk/core/execute.py +207 -0
  93. hpcflow/sdk/core/json_like.py +809 -0
  94. hpcflow/sdk/core/loop.py +1320 -0
  95. hpcflow/sdk/core/loop_cache.py +282 -0
  96. hpcflow/sdk/core/object_list.py +933 -0
  97. hpcflow/sdk/core/parameters.py +3371 -0
  98. hpcflow/sdk/core/rule.py +196 -0
  99. hpcflow/sdk/core/run_dir_files.py +57 -0
  100. hpcflow/sdk/core/skip_reason.py +7 -0
  101. hpcflow/sdk/core/task.py +3792 -0
  102. hpcflow/sdk/core/task_schema.py +993 -0
  103. hpcflow/sdk/core/test_utils.py +538 -0
  104. hpcflow/sdk/core/types.py +447 -0
  105. hpcflow/sdk/core/utils.py +1207 -0
  106. hpcflow/sdk/core/validation.py +87 -0
  107. hpcflow/sdk/core/values.py +477 -0
  108. hpcflow/sdk/core/workflow.py +4820 -0
  109. hpcflow/sdk/core/zarr_io.py +206 -0
  110. hpcflow/sdk/data/__init__.py +13 -0
  111. hpcflow/sdk/data/config_file_schema.yaml +34 -0
  112. hpcflow/sdk/data/config_schema.yaml +260 -0
  113. hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
  114. hpcflow/sdk/data/files_spec_schema.yaml +5 -0
  115. hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
  116. hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
  117. hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
  118. hpcflow/sdk/demo/__init__.py +3 -0
  119. hpcflow/sdk/demo/cli.py +242 -0
  120. hpcflow/sdk/helper/__init__.py +3 -0
  121. hpcflow/sdk/helper/cli.py +137 -0
  122. hpcflow/sdk/helper/helper.py +300 -0
  123. hpcflow/sdk/helper/watcher.py +192 -0
  124. hpcflow/sdk/log.py +288 -0
  125. hpcflow/sdk/persistence/__init__.py +18 -0
  126. hpcflow/sdk/persistence/base.py +2817 -0
  127. hpcflow/sdk/persistence/defaults.py +6 -0
  128. hpcflow/sdk/persistence/discovery.py +39 -0
  129. hpcflow/sdk/persistence/json.py +954 -0
  130. hpcflow/sdk/persistence/pending.py +948 -0
  131. hpcflow/sdk/persistence/store_resource.py +203 -0
  132. hpcflow/sdk/persistence/types.py +309 -0
  133. hpcflow/sdk/persistence/utils.py +73 -0
  134. hpcflow/sdk/persistence/zarr.py +2388 -0
  135. hpcflow/sdk/runtime.py +320 -0
  136. hpcflow/sdk/submission/__init__.py +3 -0
  137. hpcflow/sdk/submission/enums.py +70 -0
  138. hpcflow/sdk/submission/jobscript.py +2379 -0
  139. hpcflow/sdk/submission/schedulers/__init__.py +281 -0
  140. hpcflow/sdk/submission/schedulers/direct.py +233 -0
  141. hpcflow/sdk/submission/schedulers/sge.py +376 -0
  142. hpcflow/sdk/submission/schedulers/slurm.py +598 -0
  143. hpcflow/sdk/submission/schedulers/utils.py +25 -0
  144. hpcflow/sdk/submission/shells/__init__.py +52 -0
  145. hpcflow/sdk/submission/shells/base.py +229 -0
  146. hpcflow/sdk/submission/shells/bash.py +504 -0
  147. hpcflow/sdk/submission/shells/os_version.py +115 -0
  148. hpcflow/sdk/submission/shells/powershell.py +352 -0
  149. hpcflow/sdk/submission/submission.py +1402 -0
  150. hpcflow/sdk/submission/types.py +140 -0
  151. hpcflow/sdk/typing.py +194 -0
  152. hpcflow/sdk/utils/arrays.py +69 -0
  153. hpcflow/sdk/utils/deferred_file.py +55 -0
  154. hpcflow/sdk/utils/hashing.py +16 -0
  155. hpcflow/sdk/utils/patches.py +31 -0
  156. hpcflow/sdk/utils/strings.py +69 -0
  157. hpcflow/tests/api/test_api.py +32 -0
  158. hpcflow/tests/conftest.py +123 -0
  159. hpcflow/tests/data/__init__.py +0 -0
  160. hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
  161. hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
  162. hpcflow/tests/data/multi_path_sequences.yaml +29 -0
  163. hpcflow/tests/data/workflow_1.json +10 -0
  164. hpcflow/tests/data/workflow_1.yaml +5 -0
  165. hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
  166. hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
  167. hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
  168. hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
  169. hpcflow/tests/programs/test_programs.py +180 -0
  170. hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
  171. hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
  172. hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
  173. hpcflow/tests/scripts/test_input_file_generators.py +282 -0
  174. hpcflow/tests/scripts/test_main_scripts.py +1361 -0
  175. hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
  176. hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
  177. hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
  178. hpcflow/tests/unit/test_action.py +1066 -0
  179. hpcflow/tests/unit/test_action_rule.py +24 -0
  180. hpcflow/tests/unit/test_app.py +132 -0
  181. hpcflow/tests/unit/test_cache.py +46 -0
  182. hpcflow/tests/unit/test_cli.py +172 -0
  183. hpcflow/tests/unit/test_command.py +377 -0
  184. hpcflow/tests/unit/test_config.py +195 -0
  185. hpcflow/tests/unit/test_config_file.py +162 -0
  186. hpcflow/tests/unit/test_element.py +666 -0
  187. hpcflow/tests/unit/test_element_iteration.py +88 -0
  188. hpcflow/tests/unit/test_element_set.py +158 -0
  189. hpcflow/tests/unit/test_group.py +115 -0
  190. hpcflow/tests/unit/test_input_source.py +1479 -0
  191. hpcflow/tests/unit/test_input_value.py +398 -0
  192. hpcflow/tests/unit/test_jobscript_unit.py +757 -0
  193. hpcflow/tests/unit/test_json_like.py +1247 -0
  194. hpcflow/tests/unit/test_loop.py +2674 -0
  195. hpcflow/tests/unit/test_meta_task.py +325 -0
  196. hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
  197. hpcflow/tests/unit/test_object_list.py +116 -0
  198. hpcflow/tests/unit/test_parameter.py +243 -0
  199. hpcflow/tests/unit/test_persistence.py +664 -0
  200. hpcflow/tests/unit/test_resources.py +243 -0
  201. hpcflow/tests/unit/test_run.py +286 -0
  202. hpcflow/tests/unit/test_run_directories.py +29 -0
  203. hpcflow/tests/unit/test_runtime.py +9 -0
  204. hpcflow/tests/unit/test_schema_input.py +372 -0
  205. hpcflow/tests/unit/test_shell.py +129 -0
  206. hpcflow/tests/unit/test_slurm.py +39 -0
  207. hpcflow/tests/unit/test_submission.py +502 -0
  208. hpcflow/tests/unit/test_task.py +2560 -0
  209. hpcflow/tests/unit/test_task_schema.py +182 -0
  210. hpcflow/tests/unit/test_utils.py +616 -0
  211. hpcflow/tests/unit/test_value_sequence.py +549 -0
  212. hpcflow/tests/unit/test_values.py +91 -0
  213. hpcflow/tests/unit/test_workflow.py +827 -0
  214. hpcflow/tests/unit/test_workflow_template.py +186 -0
  215. hpcflow/tests/unit/utils/test_arrays.py +40 -0
  216. hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
  217. hpcflow/tests/unit/utils/test_hashing.py +65 -0
  218. hpcflow/tests/unit/utils/test_patches.py +5 -0
  219. hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
  220. hpcflow/tests/unit/utils/test_strings.py +97 -0
  221. hpcflow/tests/workflows/__init__.py +0 -0
  222. hpcflow/tests/workflows/test_directory_structure.py +31 -0
  223. hpcflow/tests/workflows/test_jobscript.py +355 -0
  224. hpcflow/tests/workflows/test_run_status.py +198 -0
  225. hpcflow/tests/workflows/test_skip_downstream.py +696 -0
  226. hpcflow/tests/workflows/test_submission.py +140 -0
  227. hpcflow/tests/workflows/test_workflows.py +564 -0
  228. hpcflow/tests/workflows/test_zip.py +18 -0
  229. hpcflow/viz_demo.ipynb +6794 -0
  230. hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
  231. hpcflow-0.2.0a271.dist-info/METADATA +65 -0
  232. hpcflow-0.2.0a271.dist-info/RECORD +237 -0
  233. {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
  234. hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
  235. hpcflow/api.py +0 -458
  236. hpcflow/archive/archive.py +0 -308
  237. hpcflow/archive/cloud/cloud.py +0 -47
  238. hpcflow/archive/cloud/errors.py +0 -9
  239. hpcflow/archive/cloud/providers/dropbox.py +0 -432
  240. hpcflow/archive/errors.py +0 -5
  241. hpcflow/base_db.py +0 -4
  242. hpcflow/config.py +0 -232
  243. hpcflow/copytree.py +0 -66
  244. hpcflow/data/examples/_config.yml +0 -14
  245. hpcflow/data/examples/damask/demo/1.run.yml +0 -4
  246. hpcflow/data/examples/damask/demo/2.process.yml +0 -29
  247. hpcflow/data/examples/damask/demo/geom.geom +0 -2052
  248. hpcflow/data/examples/damask/demo/load.load +0 -1
  249. hpcflow/data/examples/damask/demo/material.config +0 -185
  250. hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
  251. hpcflow/data/examples/damask/inputs/load.load +0 -1
  252. hpcflow/data/examples/damask/inputs/material.config +0 -185
  253. hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
  254. hpcflow/data/examples/damask/profiles/damask.yml +0 -4
  255. hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
  256. hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
  257. hpcflow/data/examples/damask/profiles/default.yml +0 -6
  258. hpcflow/data/examples/thinking.yml +0 -177
  259. hpcflow/errors.py +0 -2
  260. hpcflow/init_db.py +0 -37
  261. hpcflow/models.py +0 -2549
  262. hpcflow/nesting.py +0 -9
  263. hpcflow/profiles.py +0 -455
  264. hpcflow/project.py +0 -81
  265. hpcflow/scheduler.py +0 -323
  266. hpcflow/utils.py +0 -103
  267. hpcflow/validation.py +0 -167
  268. hpcflow/variables.py +0 -544
  269. hpcflow-0.1.9.dist-info/METADATA +0 -168
  270. hpcflow-0.1.9.dist-info/RECORD +0 -45
  271. hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
  272. hpcflow-0.1.9.dist-info/top_level.txt +0 -1
  273. /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
  274. /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
  275. /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
@@ -0,0 +1,1043 @@
1
+ """
2
+ Errors from the workflow system.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ import os
7
+ from collections.abc import Iterable, Mapping, Sequence
8
+ from textwrap import indent
9
+ from typing import Any, TYPE_CHECKING, Literal
10
+
11
+ from hpcflow.sdk.utils.strings import capitalise_first_letter
12
+
13
+ if TYPE_CHECKING:
14
+ from logging import Logger
15
+ from .enums import ParallelMode
16
+ from .object_list import WorkflowLoopList
17
+ from .parameters import InputSource, ValueSequence, SchemaInput
18
+ from .types import ActionData
19
+ from .task import WorkflowTask, Task
20
+
21
+
22
+ class InputValueDuplicateSequenceAddress(ValueError):
23
+ """
24
+ An InputValue has the same sequence address twice.
25
+ """
26
+
27
+ # FIXME: never used
28
+
29
+
30
+ class TaskTemplateMultipleSchemaObjectives(ValueError):
31
+ """
32
+ A TaskTemplate has multiple objectives.
33
+ """
34
+
35
+ def __init__(self, names: set[str]) -> None:
36
+ super().__init__(
37
+ f"All task schemas used within a task must have the same "
38
+ f"objective, but found multiple objectives: {sorted(names)!r}"
39
+ )
40
+
41
+
42
+ class TaskTemplateUnexpectedInput(ValueError):
43
+ """
44
+ A TaskTemplate was given unexpected input.
45
+ """
46
+
47
+ def __init__(self, unexpected_types: set[str]) -> None:
48
+ super().__init__(
49
+ f"The following input parameters are unexpected: {sorted(unexpected_types)!r}"
50
+ )
51
+
52
+
53
+ class TaskTemplateUnexpectedSequenceInput(ValueError):
54
+ """
55
+ A TaskTemplate was given an unexpected sequence.
56
+ """
57
+
58
+ def __init__(
59
+ self, inp_type: str, expected_types: set[str], seq: ValueSequence
60
+ ) -> None:
61
+ allowed_str = ", ".join(f'"{in_typ}"' for in_typ in expected_types)
62
+ super().__init__(
63
+ f"The input type {inp_type!r} specified in the following sequence"
64
+ f" path is unexpected: {seq.path!r}. Available input types are: "
65
+ f"{allowed_str}."
66
+ )
67
+
68
+
69
+ class TaskTemplateMultipleInputValues(ValueError):
70
+ """
71
+ A TaskTemplate had multiple input values bound over each other.
72
+ """
73
+
74
+ def __init__(self, msg: str) -> None:
75
+ super().__init__(msg)
76
+
77
+
78
+ class InvalidIdentifier(ValueError):
79
+ """
80
+ A bad identifier name was given.
81
+ """
82
+
83
+ def __init__(self, name: str) -> None:
84
+ super().__init__(f"Invalid string for identifier: {name!r}")
85
+
86
+
87
+ class MissingInputs(Exception):
88
+ """
89
+ Inputs were missing.
90
+
91
+ Parameters
92
+ ----------
93
+ missing_inputs:
94
+ The missing inputs.
95
+ """
96
+
97
+ # TODO: add links to doc pages for common user-exceptions?
98
+
99
+ def __init__(self, task: Task, missing_inputs: Iterable[str]) -> None:
100
+ self.missing_inputs = tuple(missing_inputs)
101
+ missing_str = ", ".join(map(repr, missing_inputs))
102
+ super().__init__(
103
+ f"Task {task.name}: the following inputs have no sources: {missing_str}."
104
+ )
105
+
106
+
107
+ class UnrequiredInputSources(ValueError):
108
+ """
109
+ Input sources were provided that were not required.
110
+
111
+ Parameters
112
+ ----------
113
+ unrequired_sources:
114
+ The input sources that were not required.
115
+ """
116
+
117
+ def __init__(self, unrequired_sources: Iterable[str]) -> None:
118
+ self.unrequired_sources = frozenset(unrequired_sources)
119
+ message = (
120
+ f"The following input sources are not required but have been specified: "
121
+ f'{", ".join(map(repr, sorted(self.unrequired_sources)))}.'
122
+ )
123
+ if any((bad := src).startswith("inputs.") for src in self.unrequired_sources):
124
+ # reminder about how to specify input sources:
125
+ message += (
126
+ f" Note that input source keys should not be specified with the "
127
+ f"'inputs.' prefix. Did you mean to specify "
128
+ f"{bad.removeprefix('inputs.')!r} instead of {bad!r}?"
129
+ )
130
+ super().__init__(message)
131
+
132
+
133
+ class ExtraInputs(Exception):
134
+ """
135
+ Extra inputs were provided.
136
+
137
+ Parameters
138
+ ----------
139
+ extra_inputs:
140
+ The extra inputs.
141
+ """
142
+
143
+ def __init__(self, extra_inputs: set[str]) -> None:
144
+ self.extra_inputs = frozenset(extra_inputs)
145
+ super().__init__(
146
+ f"The following inputs are not required, but have been passed: "
147
+ f'{", ".join(f"{typ!r}" for typ in extra_inputs)}.'
148
+ )
149
+
150
+
151
+ class UnavailableInputSource(ValueError):
152
+ """
153
+ An input source was not available.
154
+ """
155
+
156
+ def __init__(
157
+ self, source: InputSource, path: str, avail: Sequence[InputSource]
158
+ ) -> None:
159
+ super().__init__(
160
+ f"The input source {source.to_string()!r} is not "
161
+ f"available for input path {path!r}. Available "
162
+ f"input sources are: {[src.to_string() for src in avail]}."
163
+ )
164
+
165
+
166
+ class InapplicableInputSourceElementIters(ValueError):
167
+ """
168
+ An input source element iteration was inapplicable."""
169
+
170
+ def __init__(self, source: InputSource, elem_iters_IDs: Sequence[int] | None) -> None:
171
+ super().__init__(
172
+ f"The specified `element_iters` for input source "
173
+ f"{source.to_string()!r} are not all applicable. "
174
+ f"Applicable element iteration IDs for this input source "
175
+ f"are: {elem_iters_IDs!r}."
176
+ )
177
+
178
+
179
+ class NoCoincidentInputSources(ValueError):
180
+ """
181
+ Could not line up input sources to make an actual valid execution.
182
+ """
183
+
184
+ def __init__(self, name: str, task_ref: int) -> None:
185
+ super().__init__(
186
+ f"Task {name!r}: input sources from task {task_ref!r} have "
187
+ f"no coincident applicable element iterations. Consider setting "
188
+ f"the element set (or task) argument "
189
+ f"`allow_non_coincident_task_sources` to `True`, which will "
190
+ f"allow for input sources from the same task to use different "
191
+ f"(non-coinciding) subsets of element iterations from the "
192
+ f"source task."
193
+ )
194
+
195
+
196
+ class TaskTemplateInvalidNesting(ValueError):
197
+ """
198
+ Invalid nesting in a task template.
199
+ """
200
+
201
+ def __init__(self, key: str, value: float) -> None:
202
+ super().__init__(
203
+ f"`nesting_order` must be >=0 for all keys, but for key {key!r}, value "
204
+ f"of {value!r} was specified."
205
+ )
206
+
207
+
208
+ class TaskSchemaSpecValidationError(Exception):
209
+ """
210
+ A task schema failed to validate.
211
+ """
212
+
213
+ # FIXME: never used
214
+
215
+
216
+ class WorkflowSpecValidationError(Exception):
217
+ """
218
+ A workflow failed to validate.
219
+ """
220
+
221
+ # FIXME: never used
222
+
223
+
224
+ class InputSourceValidationError(Exception):
225
+ """
226
+ An input source failed to validate.
227
+ """
228
+
229
+ # FIXME: never used
230
+
231
+
232
+ class EnvironmentSpecValidationError(Exception):
233
+ """
234
+ An environment specification failed to validate.
235
+ """
236
+
237
+ # FIXME: never used
238
+
239
+
240
+ class ParameterSpecValidationError(Exception):
241
+ """
242
+ A parameter specification failed to validate.
243
+ """
244
+
245
+ # FIXME: never used
246
+
247
+
248
+ class FileSpecValidationError(Exception):
249
+ """
250
+ A file specification failed to validate.
251
+ """
252
+
253
+ # FIXME: never used
254
+
255
+
256
+ class DuplicateExecutableError(ValueError):
257
+ """
258
+ The same executable was present twice in an executable environment.
259
+ """
260
+
261
+ def __init__(self, duplicate_labels: list) -> None:
262
+ super().__init__(
263
+ f"Executables must have unique `label`s within each environment, but "
264
+ f"found label(s) multiple times: {duplicate_labels!r}"
265
+ )
266
+
267
+
268
+ class MissingCompatibleActionEnvironment(Exception):
269
+ """
270
+ Could not find a compatible action environment.
271
+ """
272
+
273
+ def __init__(self, msg: str) -> None:
274
+ super().__init__(f"No compatible environment is specified for the {msg}.")
275
+
276
+
277
+ class MissingActionEnvironment(Exception):
278
+ """
279
+ Could not find an action environment.
280
+ """
281
+
282
+ # FIXME: never used
283
+
284
+
285
+ class ActionEnvironmentMissingNameError(Exception):
286
+ """
287
+ An action environment was missing its name.
288
+ """
289
+
290
+ def __init__(self, environment: Mapping[str, Any]) -> None:
291
+ super().__init__(
292
+ "The action-environment environment specification must include a string "
293
+ "`name` key, or be specified as string that is that name. Provided "
294
+ f"environment key was {environment!r}."
295
+ )
296
+
297
+
298
+ class FromSpecMissingObjectError(Exception):
299
+ """
300
+ Missing object when deserialising from specification.
301
+ """
302
+
303
+ # FIXME: never used
304
+
305
+
306
+ class TaskSchemaMissingParameterError(Exception):
307
+ """
308
+ Parameter was missing from task schema.
309
+ """
310
+
311
+ # FIXME: never used
312
+
313
+
314
+ class ToJSONLikeChildReferenceError(Exception):
315
+ """
316
+ Failed to generate or reference a child object when converting to JSON.
317
+ """
318
+
319
+ # FIXME: never thrown
320
+
321
+
322
+ class InvalidInputSourceTaskReference(Exception):
323
+ """
324
+ Invalid input source in task reference.
325
+ """
326
+
327
+ def __init__(self, input_source: InputSource, task_ref: int | None = None) -> None:
328
+ super().__init__(
329
+ f"Input source {input_source.to_string()!r} cannot refer to the "
330
+ f"outputs of its own task!"
331
+ if task_ref is None
332
+ else f"Input source {input_source.to_string()!r} refers to a missing "
333
+ f"or inaccessible task: {task_ref!r}."
334
+ )
335
+
336
+
337
+ class WorkflowNotFoundError(Exception):
338
+ """
339
+ Could not find the workflow.
340
+ """
341
+
342
+ def __init__(self, path, fs) -> None:
343
+ super().__init__(
344
+ f"Cannot infer a store format at path {path!r} with file system {fs!r}."
345
+ )
346
+
347
+
348
+ class MalformedWorkflowError(Exception):
349
+ """
350
+ Workflow was a malformed document.
351
+ """
352
+
353
+ # TODO: use this class
354
+
355
+
356
+ class ValuesAlreadyPersistentError(Exception):
357
+ """
358
+ Trying to make a value persistent that already is so.
359
+ """
360
+
361
+ # FIXME: never used
362
+
363
+
364
+ class MalformedParameterPathError(ValueError):
365
+ """
366
+ The path to a parameter was ill-formed.
367
+ """
368
+
369
+ def __init__(self, msg: str) -> None:
370
+ super().__init__(msg)
371
+
372
+
373
+ class MalformedNestingOrderPath(ValueError):
374
+ """
375
+ A nesting order path was ill-formed.
376
+ """
377
+
378
+ def __init__(self, k: str, allowed_nesting_paths: Iterable[str]) -> None:
379
+ super().__init__(
380
+ f"Element set: nesting order path {k!r} not understood. Each key in "
381
+ f"`nesting_order` must be start with one of "
382
+ f"{sorted(allowed_nesting_paths)!r}."
383
+ )
384
+
385
+
386
+ class UnknownResourceSpecItemError(ValueError):
387
+ """
388
+ A resource specification item was not found.
389
+ """
390
+
391
+ def __init__(self, msg: str) -> None:
392
+ super().__init__(msg)
393
+
394
+
395
+ class WorkflowParameterMissingError(AttributeError):
396
+ """
397
+ A parameter to a workflow was missing.
398
+ """
399
+
400
+ # FIXME: never thrown
401
+
402
+
403
+ class WorkflowBatchUpdateFailedError(Exception):
404
+ """
405
+ An update to a workflow failed.
406
+ """
407
+
408
+ # FIXME: only throw is commented out?
409
+
410
+
411
+ class WorkflowLimitsError(ValueError):
412
+ """
413
+ Workflow hit limits.
414
+ """
415
+
416
+ # FIXME: never used
417
+
418
+
419
+ class UnsetParameterDataErrorBase(Exception):
420
+ """
421
+ Exceptions related to attempts to retrieve unset parameters.
422
+ """
423
+
424
+
425
+ class UnsetParameterDataError(UnsetParameterDataErrorBase):
426
+ """
427
+ Tried to read from an unset parameter.
428
+ """
429
+
430
+ def __init__(self, path: str | None, path_i: str) -> None:
431
+ super().__init__(
432
+ f"Element data path {path!r} resolves to unset data for "
433
+ f"(at least) data-index path: {path_i!r}."
434
+ )
435
+
436
+
437
+ class UnsetParameterFractionLimitExceededError(UnsetParameterDataErrorBase):
438
+ """
439
+ Given the specified `allow_failed_dependencies`, the fraction of failed dependencies
440
+ (unset parameter data) is too high."""
441
+
442
+ def __init__(
443
+ self,
444
+ schema_inp: SchemaInput,
445
+ task: WorkflowTask,
446
+ unset_fraction: float,
447
+ log: Logger | None = None,
448
+ ):
449
+ msg = (
450
+ f"Input {schema_inp.parameter.typ!r} of task {task.name!r}: higher "
451
+ f"proportion of dependencies failed ({unset_fraction!r}) than allowed "
452
+ f"({schema_inp.allow_failed_dependencies!r})."
453
+ )
454
+ if log:
455
+ log.info(msg)
456
+ super().__init__(msg)
457
+
458
+
459
+ class UnsetParameterNumberLimitExceededError(UnsetParameterDataErrorBase):
460
+ """
461
+ Given the specified `allow_failed_dependencies`, the number of failed dependencies
462
+ (unset parameter data) is too high."""
463
+
464
+ def __init__(
465
+ self,
466
+ schema_inp: SchemaInput,
467
+ task: WorkflowTask,
468
+ unset_num: int,
469
+ log: Logger | None = None,
470
+ ):
471
+ msg = (
472
+ f"Input {schema_inp.parameter.typ!r} of task {task.name!r}: higher number of "
473
+ f"dependencies failed ({unset_num!r}) than allowed "
474
+ f"({schema_inp.allow_failed_dependencies!r})."
475
+ )
476
+ if log:
477
+ log.info(msg)
478
+ super().__init__(msg)
479
+
480
+
481
+ class LoopAlreadyExistsError(Exception):
482
+ """
483
+ A particular loop (or its name) already exists.
484
+ """
485
+
486
+ def __init__(self, loop_name: str, loops: WorkflowLoopList) -> None:
487
+ super().__init__(
488
+ f"A loop with the name {loop_name!r} already exists in the workflow: "
489
+ f"{getattr(loops, loop_name)!r}."
490
+ )
491
+
492
+
493
+ class LoopTaskSubsetError(ValueError):
494
+ """
495
+ Problem constructing a subset of a task for a loop.
496
+ """
497
+
498
+ def __init__(self, loop_name: str, task_indices: Sequence[int]) -> None:
499
+ super().__init__(
500
+ f"Loop {loop_name!r}: task subset must be an ascending contiguous range, "
501
+ f"but specified task indices were: {task_indices!r}."
502
+ )
503
+
504
+
505
+ class SchedulerVersionsFailure(RuntimeError):
506
+ """We couldn't get the scheduler and or shell versions."""
507
+
508
+ # FIXME: unused
509
+
510
+ def __init__(self, message: str) -> None:
511
+ self.message = message
512
+ super().__init__(message)
513
+
514
+
515
+ class JobscriptSubmissionFailure(RuntimeError):
516
+ """
517
+ A job script could not be submitted to the scheduler.
518
+ """
519
+
520
+ def __init__(
521
+ self,
522
+ message: str,
523
+ *,
524
+ submit_cmd: list[str],
525
+ js_idx: int,
526
+ js_path: str,
527
+ stdout: str | None = None,
528
+ stderr: str | None = None,
529
+ subprocess_exc: Exception | None = None,
530
+ job_ID_parse_exc: Exception | None = None,
531
+ ):
532
+ self.message = message
533
+ self.submit_cmd = submit_cmd
534
+ self.js_idx = js_idx
535
+ self.js_path = js_path
536
+ self.stdout = stdout
537
+ self.stderr = stderr
538
+ self.subprocess_exc = subprocess_exc
539
+ self.job_ID_parse_exc = job_ID_parse_exc
540
+ super().__init__(message)
541
+
542
+
543
+ class SubmissionFailure(RuntimeError):
544
+ """
545
+ A job submission failed.
546
+ """
547
+
548
+ def __init__(
549
+ self,
550
+ sub_idx: int,
551
+ submitted_js_idx: Sequence[int],
552
+ exceptions: Iterable[JobscriptSubmissionFailure],
553
+ ) -> None:
554
+ msg = f"Some jobscripts in submission index {sub_idx} could not be submitted"
555
+ if submitted_js_idx:
556
+ msg += f" (but jobscripts {submitted_js_idx} were submitted successfully):"
557
+ else:
558
+ msg += ":"
559
+
560
+ msg += "\n"
561
+ for sub_err in exceptions:
562
+ msg += (
563
+ f"Jobscript {sub_err.js_idx} at path: {str(sub_err.js_path)!r}\n"
564
+ f"Submit command: {sub_err.submit_cmd!r}.\n"
565
+ f"Reason: {sub_err.message!r}\n"
566
+ )
567
+ if sub_err.subprocess_exc is not None:
568
+ msg += f"Subprocess exception: {sub_err.subprocess_exc}\n"
569
+ if sub_err.job_ID_parse_exc is not None:
570
+ msg += f"Subprocess job ID parse exception: {sub_err.job_ID_parse_exc}\n"
571
+ if sub_err.job_ID_parse_exc is not None:
572
+ msg += f"Job ID parse exception: {sub_err.job_ID_parse_exc}\n"
573
+ if sub_err.stdout:
574
+ msg += f"Submission stdout:\n{indent(sub_err.stdout, ' ')}\n"
575
+ if sub_err.stderr:
576
+ msg += f"Submission stderr:\n{indent(sub_err.stderr, ' ')}\n"
577
+ self.message = msg
578
+ super().__init__(msg)
579
+
580
+
581
+ class WorkflowSubmissionFailure(RuntimeError):
582
+ """
583
+ A workflow submission failed.
584
+ """
585
+
586
+ def __init__(self, exceptions: Sequence[SubmissionFailure]) -> None:
587
+ super().__init__("\n" + "\n\n".join(exn.message for exn in exceptions))
588
+
589
+
590
+ class ResourceValidationError(ValueError):
591
+ """An incompatible resource requested by the user."""
592
+
593
+
594
+ class UnsupportedOSError(ResourceValidationError):
595
+ """This machine is not of the requested OS."""
596
+
597
+ def __init__(self, os_name: str) -> None:
598
+ message = (
599
+ f"OS {os_name!r} is not compatible with this machine/instance with OS: "
600
+ f"{os.name!r}."
601
+ )
602
+ super().__init__(message)
603
+ self.os_name = os_name
604
+
605
+
606
+ class UnsupportedShellError(ResourceValidationError):
607
+ """We don't support this shell on this OS."""
608
+
609
+ def __init__(self, shell: str, supported: Iterable[str]) -> None:
610
+ sup = set(supported)
611
+ message = (
612
+ f"Shell {shell!r} is not supported on this machine/instance. Supported "
613
+ f"shells are: {sup!r}."
614
+ )
615
+ super().__init__(message)
616
+ self.shell = shell
617
+ self.supported = frozenset(sup)
618
+
619
+
620
+ class UnsupportedSchedulerError(ResourceValidationError):
621
+ """This scheduler is not supported on this machine according to the config.
622
+
623
+ This is also raised in config validation when attempting to add a scheduler that is
624
+ not known for this OS.
625
+
626
+ """
627
+
628
+ def __init__(
629
+ self,
630
+ scheduler: str,
631
+ supported: Iterable[str] | None = None,
632
+ available: Iterable[str] | None = None,
633
+ ) -> None:
634
+ if supported is not None:
635
+ message = (
636
+ f"Scheduler {scheduler!r} is not supported on this machine/instance. "
637
+ f"Supported schedulers according to the app configuration are: "
638
+ f"{supported!r}."
639
+ )
640
+ elif available is not None:
641
+ message = (
642
+ f"Scheduler {scheduler!r} is not supported on this OS. Schedulers "
643
+ f"compatible with this OS are: {available!r}."
644
+ )
645
+ super().__init__(message)
646
+ self.scheduler = scheduler
647
+ self.supported = None if supported is None else tuple(supported)
648
+ self.available = None if available is None else tuple(available)
649
+
650
+
651
+ class UnknownSGEPEError(ResourceValidationError):
652
+ """
653
+ Miscellaneous error from SGE parallel environment.
654
+ """
655
+
656
+ def __init__(self, env_name: str, all_env_names: Iterable[str]) -> None:
657
+ super().__init__(
658
+ f"The SGE parallel environment {env_name!r} is not "
659
+ f"specified in the configuration. Specified parallel environments "
660
+ f"are {sorted(all_env_names)!r}."
661
+ )
662
+
663
+
664
+ class IncompatibleSGEPEError(ResourceValidationError):
665
+ """
666
+ The SGE parallel environment selected is incompatible.
667
+ """
668
+
669
+ def __init__(self, env_name: str, num_cores: int | None) -> None:
670
+ super().__init__(
671
+ f"The SGE parallel environment {env_name!r} is not "
672
+ f"compatible with the number of cores requested: "
673
+ f"{num_cores!r}."
674
+ )
675
+
676
+
677
+ class NoCompatibleSGEPEError(ResourceValidationError):
678
+ """
679
+ No SGE parallel environment is compatible with request.
680
+ """
681
+
682
+ def __init__(self, num_cores: int | None) -> None:
683
+ super().__init__(
684
+ f"No compatible SGE parallel environment could be found for the "
685
+ f"specified `num_cores` ({num_cores!r})."
686
+ )
687
+
688
+
689
+ class IncompatibleParallelModeError(ResourceValidationError):
690
+ """
691
+ The parallel mode is incompatible.
692
+ """
693
+
694
+ def __init__(self, parallel_mode: ParallelMode) -> None:
695
+ super().__init__(
696
+ f"For the {parallel_mode.name.lower()} parallel mode, "
697
+ f"only a single node may be requested."
698
+ )
699
+
700
+
701
+ class UnknownSLURMPartitionError(ResourceValidationError):
702
+ """
703
+ The requested SLURM partition isn't known.
704
+ """
705
+
706
+ def __init__(self, part_name: str, all_parts: Iterable[str]) -> None:
707
+ super().__init__(
708
+ f"The SLURM partition {part_name!r} is not "
709
+ f"specified in the configuration. Specified partitions are "
710
+ f"{sorted(all_parts)!r}."
711
+ )
712
+
713
+
714
+ class IncompatibleSLURMPartitionError(ResourceValidationError):
715
+ """
716
+ The requested SLURM partition is incompatible.
717
+ """
718
+
719
+ def __init__(self, part_name: str, attr_kind: str, value) -> None:
720
+ super().__init__(
721
+ f"The SLURM partition {part_name!r} is not "
722
+ f"compatible with the {attr_kind} requested: {value!r}."
723
+ )
724
+
725
+
726
+ class IncompatibleSLURMArgumentsError(ResourceValidationError):
727
+ """
728
+ The SLURM arguments are incompatible with each other.
729
+ """
730
+
731
+ def __init__(self, msg: str) -> None:
732
+ super().__init__(msg)
733
+
734
+
735
+ class _MissingStoreItemError(ValueError):
736
+ def __init__(self, id_lst: Iterable[int], item_type: str) -> None:
737
+ message = (
738
+ f"Store {item_type}s with the following IDs do not all exist: {id_lst!r}"
739
+ )
740
+ super().__init__(message)
741
+ self.id_lst = id_lst
742
+
743
+
744
+ class MissingStoreTaskError(_MissingStoreItemError):
745
+ """Some task IDs do not exist."""
746
+
747
+ _item_type = "task"
748
+
749
+ def __init__(self, id_lst: Iterable[int]) -> None:
750
+ super().__init__(id_lst, self._item_type)
751
+
752
+
753
+ class MissingStoreElementError(_MissingStoreItemError):
754
+ """Some element IDs do not exist."""
755
+
756
+ _item_type = "element"
757
+
758
+ def __init__(self, id_lst: Iterable[int]) -> None:
759
+ super().__init__(id_lst, self._item_type)
760
+
761
+
762
+ class MissingStoreElementIterationError(_MissingStoreItemError):
763
+ """Some element iteration IDs do not exist."""
764
+
765
+ _item_type = "element iteration"
766
+
767
+ def __init__(self, id_lst: Iterable[int]) -> None:
768
+ super().__init__(id_lst, self._item_type)
769
+
770
+
771
+ class MissingStoreEARError(_MissingStoreItemError):
772
+ """Some EAR IDs do not exist."""
773
+
774
+ _item_type = "EAR"
775
+
776
+ def __init__(self, id_lst: Iterable[int]) -> None:
777
+ super().__init__(id_lst, self._item_type)
778
+
779
+
780
+ class MissingParameterData(_MissingStoreItemError):
781
+ """Some parameter IDs do not exist"""
782
+
783
+ _item_type = "parameter"
784
+
785
+ def __init__(self, id_lst: Iterable[int]) -> None:
786
+ super().__init__(id_lst, self._item_type)
787
+
788
+
789
+ class ParametersMetadataReadOnlyError(RuntimeError):
790
+ pass
791
+
792
+
793
+ class NotSubmitMachineError(RuntimeError):
794
+ """
795
+ The requested machine can't be submitted to.
796
+ """
797
+
798
+ def __init__(self) -> None:
799
+ super().__init__(
800
+ "Cannot get active state of the jobscript because the current machine "
801
+ "is not the machine on which the jobscript was submitted."
802
+ )
803
+
804
+
805
+ class RunNotAbortableError(ValueError):
806
+ """
807
+ Cannot abort the run.
808
+ """
809
+
810
+ def __init__(self) -> None:
811
+ super().__init__(
812
+ "The run is not defined as abortable in the task schema, so it cannot "
813
+ "be aborted."
814
+ )
815
+
816
+
817
+ class NoCLIFormatMethodError(AttributeError):
818
+ """
819
+ Some CLI class lacks a format method
820
+ """
821
+
822
+ def __init__(self, method: str, inp_val: object) -> None:
823
+ super().__init__(
824
+ f"No CLI format method {method!r} exists for the object {inp_val!r}."
825
+ )
826
+
827
+
828
+ class ContainerKeyError(KeyError):
829
+ """
830
+ A key could not be mapped in a container.
831
+
832
+ Parameters
833
+ ----------
834
+ path:
835
+ The path whose resolution failed.
836
+ """
837
+
838
+ def __init__(self, path: list[str]) -> None:
839
+ self.path = path
840
+ super().__init__()
841
+
842
+
843
+ class MayNeedObjectError(Exception):
844
+ """
845
+ An object is needed but not present.
846
+
847
+ Parameters
848
+ ----------
849
+ path:
850
+ The path whose resolution failed.
851
+ """
852
+
853
+ def __init__(self, path: str) -> None:
854
+ self.path = path
855
+ super().__init__()
856
+
857
+
858
+ class NoAvailableElementSetsError(Exception):
859
+ """
860
+ No element set is available.
861
+ """
862
+
863
+ def __init__(self) -> None:
864
+ super().__init__()
865
+
866
+
867
+ class OutputFileParserNoOutputError(ValueError):
868
+ """
869
+ There was no output for the output file parser to parse.
870
+ """
871
+
872
+ def __init__(self) -> None:
873
+ super().__init__()
874
+
875
+
876
+ class SubmissionEnvironmentError(ValueError):
877
+ """
878
+ Raised when submitting a workflow on a machine without a compatible environment.
879
+ """
880
+
881
+
882
+ def _spec_to_ref(env_spec: Mapping[str, Any]):
883
+ non_name_spec = {k: v for k, v in env_spec.items() if k != "name"}
884
+ spec_str = f" with specifiers {non_name_spec!r}" if non_name_spec else ""
885
+ return f"{env_spec['name']!r}{spec_str}"
886
+
887
+
888
+ class MissingEnvironmentExecutableError(SubmissionEnvironmentError):
889
+ """
890
+ The environment does not have the requested executable at all.
891
+ """
892
+
893
+ def __init__(self, env_spec: Mapping[str, Any], exec_label: str) -> None:
894
+ super().__init__(
895
+ f"The environment {_spec_to_ref(env_spec)} as defined on this machine has no "
896
+ f"executable labelled {exec_label!r}, which is required for this "
897
+ f"submission, so the submission cannot be created."
898
+ )
899
+
900
+
901
+ class MissingEnvironmentExecutableInstanceError(SubmissionEnvironmentError):
902
+ """
903
+ The environment does not have a suitable instance of the requested executable.
904
+ """
905
+
906
+ def __init__(
907
+ self, env_spec: Mapping[str, Any], exec_label: str, js_idx: int, res: dict
908
+ ) -> None:
909
+ super().__init__(
910
+ f"No matching executable instances found for executable "
911
+ f"{exec_label!r} of environment {_spec_to_ref(env_spec)} for jobscript "
912
+ f"index {js_idx!r} with requested resources {res!r}."
913
+ )
914
+
915
+
916
+ class MissingEnvironmentError(SubmissionEnvironmentError):
917
+ """
918
+ There is no environment with that name.
919
+ """
920
+
921
+ def __init__(self, env_spec: Mapping[str, Any]) -> None:
922
+ super().__init__(
923
+ f"The environment {_spec_to_ref(env_spec)} is not defined on this machine, so the "
924
+ f"submission cannot be created."
925
+ )
926
+
927
+
928
+ class UnsupportedActionDataFormat(ValueError):
929
+ """
930
+ That format of script or program data is not supported.
931
+ """
932
+
933
+ def __init__(
934
+ self,
935
+ type: Literal["script", "program"],
936
+ data: ActionData,
937
+ kind: Literal["input", "output"],
938
+ name: str,
939
+ formats: tuple[str, ...],
940
+ ) -> None:
941
+ super().__init__(
942
+ f"{capitalise_first_letter(type)} data format {data!r} for {kind} parameter "
943
+ f"{name!r} is not understood. Available {type} data formats are: "
944
+ f"{formats!r}."
945
+ )
946
+
947
+
948
+ class UnknownActionDataParameter(ValueError):
949
+ """
950
+ Unknown parameter in script or program data.
951
+ """
952
+
953
+ def __init__(
954
+ self,
955
+ type: Literal["script", "program"],
956
+ name: str,
957
+ direction: Literal["in", "out"],
958
+ param_names: Sequence[str],
959
+ ) -> None:
960
+ super().__init__(
961
+ f"{capitalise_first_letter(type)} data {direction} parameter {name!r} is not "
962
+ f"a known parameter of the action. Parameters are: {param_names!r}."
963
+ )
964
+
965
+
966
+ class UnknownActionDataKey(ValueError):
967
+ """
968
+ Unknown key in script data.
969
+ """
970
+
971
+ def __init__(
972
+ self, type: Literal["script", "program"], key: str, allowed_keys: Sequence[str]
973
+ ) -> None:
974
+ super().__init__(
975
+ f"{capitalise_first_letter(type)} data key {key!r} is not understood. "
976
+ f"Allowed keys are: {allowed_keys!r}."
977
+ )
978
+
979
+
980
+ class MissingVariableSubstitutionError(KeyError):
981
+ """
982
+ No definition available of a variable being substituted.
983
+ """
984
+
985
+ def __init__(self, var_name: str, variables: Iterable[str]) -> None:
986
+ super().__init__(
987
+ f"The variable {var_name!r} referenced in the string does not match "
988
+ f"any of the provided variables: {sorted(variables)!r}."
989
+ )
990
+
991
+
992
+ class EnvironmentPresetUnknownEnvironmentError(ValueError):
993
+ """
994
+ An environment preset could not be resolved to an execution environment.
995
+ """
996
+
997
+ def __init__(self, name: str, bad_envs: Iterable[str]) -> None:
998
+ super().__init__(
999
+ f"Task schema {name} has environment presets that refer to one "
1000
+ f"or more environments that are not referenced in any of the task "
1001
+ f"schema's actions: {', '.join(f'{env!r}' for env in sorted(bad_envs))}."
1002
+ )
1003
+
1004
+
1005
+ class UnknownEnvironmentPresetError(ValueError):
1006
+ """
1007
+ An execution environment was unknown.
1008
+ """
1009
+
1010
+ def __init__(self, preset_name: str, schema_name: str) -> None:
1011
+ super().__init__(
1012
+ f"There is no environment preset named {preset_name!r} defined "
1013
+ f"in the task schema {schema_name}."
1014
+ )
1015
+
1016
+
1017
+ class MultipleEnvironmentsError(ValueError):
1018
+ """
1019
+ Multiple applicable execution environments exist.
1020
+ """
1021
+
1022
+ def __init__(self, env_spec: Mapping[str, Any]) -> None:
1023
+ super().__init__(
1024
+ f"Multiple environments {_spec_to_ref(env_spec)} are defined on this machine."
1025
+ )
1026
+
1027
+
1028
+ class MissingElementGroup(ValueError):
1029
+ """
1030
+ An element group should exist but doesn't.
1031
+ """
1032
+
1033
+ def __init__(self, task_name: str, group_name: str, input_path: str) -> None:
1034
+ super().__init__(
1035
+ f"Adding elements to task {task_name!r}: "
1036
+ f"no element group named {group_name!r} found for input {input_path!r}."
1037
+ )
1038
+
1039
+
1040
+ class YAMLError(ValueError):
1041
+ """
1042
+ A problem with parsing a YAML file.
1043
+ """