hpcflow 0.1.15__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 -461
  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.15.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 -490
  236. hpcflow/archive/archive.py +0 -307
  237. hpcflow/archive/cloud/cloud.py +0 -45
  238. hpcflow/archive/cloud/errors.py +0 -9
  239. hpcflow/archive/cloud/providers/dropbox.py +0 -427
  240. hpcflow/archive/errors.py +0 -5
  241. hpcflow/base_db.py +0 -4
  242. hpcflow/config.py +0 -233
  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 -2595
  262. hpcflow/nesting.py +0 -9
  263. hpcflow/profiles.py +0 -455
  264. hpcflow/project.py +0 -81
  265. hpcflow/scheduler.py +0 -322
  266. hpcflow/utils.py +0 -103
  267. hpcflow/validation.py +0 -166
  268. hpcflow/variables.py +0 -543
  269. hpcflow-0.1.15.dist-info/METADATA +0 -168
  270. hpcflow-0.1.15.dist-info/RECORD +0 -45
  271. hpcflow-0.1.15.dist-info/entry_points.txt +0 -8
  272. hpcflow-0.1.15.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
hpcflow/nesting.py DELETED
@@ -1,9 +0,0 @@
1
- """`hpcflow.nesting.py`"""
2
-
3
- from enum import Enum
4
-
5
-
6
- class NestingType(Enum):
7
- """Class to represent the possible nesting types of a command group."""
8
- hold = 'hold'
9
- nest = 'nest'
hpcflow/profiles.py DELETED
@@ -1,455 +0,0 @@
1
- """`hpcflow.profiles.py`
2
-
3
- Module containing code associated with using YAML file job profiles to
4
- construct a Workflow.
5
-
6
- """
7
-
8
- from pathlib import Path
9
-
10
- from ruamel import yaml
11
-
12
- from hpcflow.config import Config as CONFIG
13
- from hpcflow.validation import (
14
- validate_task_multiplicity,
15
- validate_job_profile,
16
- validate_job_profile_list,
17
- )
18
- from hpcflow.variables import (
19
- select_cmd_group_var_definitions,
20
- get_all_var_defns_from_lookup,
21
- get_variabled_filename_regex,
22
- find_variabled_filenames,
23
- )
24
- from hpcflow.models import CommandGroup
25
-
26
-
27
- def resolve_root_archive(root_archive_name, archives):
28
- """Resolve archive definition from the config file for the root archive.
29
-
30
- Returns
31
- -------
32
- arch_idx : int
33
- The index of the root archive in the archives list.
34
-
35
- """
36
-
37
- try:
38
- arch_defn = CONFIG.get('archive_locations')[root_archive_name]
39
- except KeyError:
40
- msg = ('An archive called "{}" was not found in the '
41
- 'configuration file. Available archives are: {}')
42
- raise ValueError(msg.format(root_archive_name, CONFIG.get('archive_locations')))
43
-
44
- existing_idx = None
45
- for k_idx, k in enumerate(archives):
46
- if k['name'] == root_archive_name:
47
- existing_idx = k_idx
48
- break
49
-
50
- if existing_idx is not None:
51
- arch_idx = existing_idx
52
- else:
53
- archives.append({
54
- 'name': root_archive_name,
55
- **arch_defn,
56
- })
57
- arch_idx = len(archives) - 1
58
-
59
- return arch_idx
60
-
61
-
62
- def resolve_archives(cmd_group, archives, profile_archives):
63
- """Resolve archive definition from the config file and add to the archives
64
- list"""
65
-
66
- arch_name = cmd_group.pop('archive', None)
67
- if arch_name:
68
- try:
69
- arch_defn = {
70
- **CONFIG.get('archive_locations'),
71
- **(profile_archives or {}),
72
- }[arch_name]
73
- except KeyError:
74
- msg = ('An archive called "{}" was not found in the '
75
- 'configuration file. Available archives are: {}')
76
- raise ValueError(msg.format(arch_name, CONFIG.get('archive_locations')))
77
-
78
- existing_idx = None
79
- for k_idx, k in enumerate(archives):
80
- if k['name'] == arch_name:
81
- existing_idx = k_idx
82
- break
83
-
84
- if existing_idx is not None:
85
- cmd_group['archive_idx'] = existing_idx
86
- else:
87
- archives.append({
88
- 'name': arch_name,
89
- **arch_defn,
90
- })
91
- cmd_group['archive_idx'] = len(archives) - 1
92
-
93
- else:
94
- cmd_group.pop('archive_excludes', None)
95
-
96
-
97
- def normalise_commands(commands):
98
- """Normalise command group commands to a list of dicts."""
99
-
100
- if isinstance(commands, str):
101
- commands = [{'line': i} for i in commands.splitlines()]
102
-
103
- if not isinstance(commands, list):
104
- msg = '`commands` must be a (optionally multiline) string or a list of dict.'
105
- raise ValueError(msg)
106
-
107
- for cmd_idx, cmd in enumerate(commands):
108
- if not isinstance(cmd, dict):
109
- msg = 'Each element in `commands` must be dict.'
110
- raise ValueError(msg)
111
-
112
- ALLOWED = ['line', 'parallel_mode', 'subshell']
113
- ALLOWED_fmt = ', '.join([f'{i!r}' for i in ALLOWED])
114
- bad_keys = set(cmd.keys()) - set(ALLOWED)
115
- if bad_keys:
116
- bad_keys_fmt = ', '.join([f'{i!r}' for i in bad_keys])
117
- msg = (f'Each element in `commands` must be a dict with allowed keys: '
118
- f'{ALLOWED_fmt}. Unknown keys specified: {bad_keys_fmt}.')
119
- raise ValueError(msg)
120
-
121
- if 'subshell' in cmd:
122
- if ('line' or 'parallel_mode') in cmd:
123
- msg = (f'If `subshell` is specified in a `commands` list element, no '
124
- f'other keys are permitted in the list element.')
125
- raise ValueError(msg)
126
- commands[cmd_idx]['subshell'] = normalise_commands(cmd['subshell'])
127
-
128
- elif 'line' in cmd:
129
- if not cmd['line']:
130
- raise ValueError(f'The `line` key in each element in `commands` must be '
131
- f'non-null.')
132
-
133
- return commands
134
-
135
-
136
- def prepare_workflow_dict(*profile_dicts):
137
- """Prepare the workflow dict for initialisation as a Workflow object."""
138
-
139
- # Form command group list, where profile-level parameters are copied to the
140
- # child command groups, but equivalent command group parameters take
141
- # precedence. Also extract out variable definitions.
142
- var_definitions = {}
143
- command_groups = []
144
- pre_commands = []
145
- archives = []
146
- root_arch_name = None
147
- loop = None
148
- parallel_modes = None
149
- root_arch_exc = []
150
- root_arch_num = 0
151
- profile_files = []
152
- for i in profile_dicts:
153
-
154
- pre_commands.extend(i.get('pre_commands') or [])
155
-
156
- # `root_archive` should be specified at most once across all profiles
157
- # regardless of whether the value is set to `null`:
158
- if 'root_archive' in i:
159
- if root_arch_num == 1:
160
- msg = ('`root_archive` must be specified at most once across '
161
- 'all job profiles.')
162
- raise ValueError(msg)
163
- else:
164
- root_arch_num += 1
165
- root_arch_name = i['root_archive']
166
-
167
- if i.get('root_archive_excludes'):
168
- root_arch_exc.extend(i['root_archive_excludes'])
169
-
170
- if 'loop' in i:
171
- loop = i['loop']
172
- if 'parallel_modes' in i:
173
- parallel_modes = i['parallel_modes']
174
-
175
- profile_cmd_groups = []
176
- # Form command group list:
177
- exec_order_add = len(command_groups)
178
- for cmd_group_idx, j in enumerate(i['command_groups']):
179
-
180
- # Populate with defaults first:
181
- cmd_group = {**CONFIG.get('cmd_group_defaults')}
182
-
183
- # Overwrite with profile-level parameters:
184
- SHARED_KEYS = (
185
- set(CONFIG.get('profile_keys_allowed')) &
186
- set(CONFIG.get('cmd_group_keys_allowed'))
187
- )
188
- for k in SHARED_KEYS:
189
- if i.get(k) is not None:
190
- cmd_group.update({k: i[k]})
191
- # Overwrite with command-level parameters:
192
- cmd_group.update(**j)
193
-
194
- if 'exec_order' not in cmd_group:
195
- cmd_group['exec_order'] = cmd_group_idx
196
-
197
- cmd_group['exec_order'] += exec_order_add
198
-
199
- # Combine scheduler options with scheduler name:
200
- cmd_group['scheduler'] = {
201
- 'name': cmd_group['scheduler'],
202
- 'options': cmd_group.pop('scheduler_options'),
203
- 'output_dir': cmd_group.pop('output_dir'),
204
- 'error_dir': cmd_group.pop('error_dir'),
205
- }
206
-
207
- # Normalise env: if env is a string, split by newlines:
208
- env = cmd_group.get('environment')
209
- if isinstance(env, str):
210
- cmd_group['environment'] = env.splitlines()
211
-
212
- # Normalise commands:
213
- cmd_group['commands'] = normalise_commands(cmd_group['commands'])
214
-
215
- profile_cmd_groups.append(cmd_group)
216
-
217
- resolve_archives(cmd_group, archives, i.get('archive_locations'))
218
-
219
- command_groups.extend(profile_cmd_groups)
220
-
221
- # Extract var_defs from profile and from var lookup given var scope:
222
- var_scope = i.get('variable_scope')
223
- var_defns_all = i.get('variables', {})
224
- var_defns_all.update(get_all_var_defns_from_lookup(var_scope))
225
-
226
- for j in profile_cmd_groups:
227
-
228
- cmd_group_var_defns = select_cmd_group_var_definitions(
229
- var_defns_all,
230
- CommandGroup.get_command_lines(j['commands']),
231
- j['directory']
232
- )
233
-
234
- var_definitions.update(cmd_group_var_defns)
235
-
236
- if 'profile_file' in i:
237
- profile_files.append(i['profile_file'])
238
-
239
- root_arch_idx = None
240
- if root_arch_name:
241
- root_arch_idx = resolve_root_archive(root_arch_name, archives)
242
-
243
- workflow = {
244
- 'command_groups': command_groups,
245
- 'var_definitions': var_definitions,
246
- 'pre_commands': pre_commands,
247
- 'archives': archives,
248
- 'root_archive_idx': root_arch_idx,
249
- 'root_archive_excludes': root_arch_exc,
250
- 'profile_files': profile_files,
251
- 'parallel_modes': parallel_modes,
252
- }
253
-
254
- if loop:
255
- workflow.update({'loop': loop})
256
-
257
- return workflow
258
-
259
-
260
- def parse_job_profiles(dir_path=None, profile_list=None):
261
- """Parse YAML file profiles into a form suitable for `models.Workflow`
262
- initialisation.
263
-
264
- Parameters
265
- ----------
266
- dir_path : Path
267
- The directory in which the Workflow will be generated. In addition to
268
- the profile file paths explicitly declared in the `profiles` parameter,
269
- this directory (`dir_path`) will be searched for profile files.
270
- profile_list : list of (str or Path), optional
271
- List of YAML profile file paths to be used to construct the Workflow.
272
- If `None` and if no profiles are found in `dir_path` according to the
273
- global configuration for the expected format of profile files, then a
274
- `ValueError` is raised.
275
-
276
- Returns
277
- -------
278
- workflow_dict : dict
279
- Dict representing the workflow, with two keys: `command_groups` and
280
- `variable_definitions`.
281
-
282
- """
283
-
284
- profile_matches = parse_profile_filenames(dir_path, profile_list)
285
-
286
- print(f'profile_matches: {profile_matches}')
287
-
288
- all_profiles = []
289
- for job_profile_path, var_values in profile_matches.items():
290
- all_profiles.append(
291
- resolve_job_profile(job_profile_path, var_values)
292
- )
293
-
294
- all_profiles = validate_job_profile_list(all_profiles)
295
-
296
- return prepare_workflow_dict(*all_profiles)
297
-
298
-
299
- def parse_profile_filenames(dir_path, profile_list=None):
300
- """Resolve name and order variables embedded in profile filenames."""
301
-
302
- fn_fmt = CONFIG.get('profile_filename_fmt')
303
- var_delims = CONFIG.get('variable_delimiters')
304
-
305
- # There are only two allowed profile filename-encodable variables:
306
- var_value = {
307
- 'profile_name': {
308
- 'fmt': r'[A-Za-z0-9_\-]*',
309
- 'type': str,
310
- },
311
- 'profile_order': {
312
- 'fmt': r'[0-9]+',
313
- 'type': int,
314
- },
315
- }
316
- var_value_fmt = {k: v['fmt'] for k, v in var_value.items()}
317
- var_value_type = {k: v['type'] for k, v in var_value.items()}
318
-
319
- fn_regex, var_names = get_variabled_filename_regex(
320
- fn_fmt, var_delims, var_value_fmt)
321
-
322
- if profile_list:
323
- # Check given profile file names exist and match expected format:
324
- profile_matches = find_variabled_filenames(
325
- profile_list,
326
- fn_regex,
327
- var_names,
328
- var_value_type
329
- )
330
-
331
- else:
332
- # Search `dir_path` for matching file names:
333
- dir_files = [i for i in dir_path.iterdir() if i.is_file()]
334
- profile_matches = find_variabled_filenames(
335
- dir_files,
336
- fn_regex,
337
- var_names,
338
- var_value_type,
339
- all_must_match=False,
340
- check_exists=False
341
- )
342
-
343
- if not profile_matches:
344
- msg = ('No profiles found in directory: "{}"')
345
- raise ValueError(msg.format(dir_path))
346
-
347
- return profile_matches
348
-
349
-
350
- def resolve_job_profile(job_profile_path, filename_var_values,
351
- common_profiles_dir=None):
352
- """Resolve the inheritance tree of a job profile and do basic validation.
353
-
354
- Parameters
355
- ----------
356
- job_profile_path : str or Path
357
- Path to the job profile file, which may inherit from common profiles.
358
- filename_var_values : dict
359
- Dictionary that maps variable names to their values as embedded in the
360
- job profile file name.
361
- common_profiles_dir : str or Path
362
- Directory that contains common profiles. By default, set to `None`, in
363
- which case, the default profile directory is used (which is within the
364
- hpcflow configuration directory).
365
-
366
- Returns
367
- -------
368
- merged_profile : dict
369
- Dictionary of the resolved job profile.
370
-
371
- """
372
-
373
- job_profile_path = Path(job_profile_path)
374
- if common_profiles_dir:
375
- common_profiles_dir = Path(common_profiles_dir)
376
- else:
377
- common_profiles_dir = CONFIG.get('profiles_dir')
378
-
379
- # Get a list of all common profile file names (without any extension):
380
- prof_names = []
381
- for prof in common_profiles_dir.iterdir():
382
- prof_names.append(prof.stem)
383
-
384
- profile_inheritance = [job_profile_path.stem]
385
-
386
- with job_profile_path.open() as handle:
387
- job_profile = yaml.safe_load(handle)
388
-
389
- # Form a list of data from each profile in the inheritance tree:
390
- all_profiles = [job_profile]
391
- parent_profile = job_profile
392
- while 'inherits' in parent_profile:
393
-
394
- parent_profile_name = parent_profile['inherits']
395
- profile_inheritance.append(parent_profile_name)
396
-
397
- if parent_profile_name not in prof_names:
398
- msg = ('Cannot find profile "{}" in the profile directory "{}".')
399
- raise ValueError(msg.format(
400
- parent_profile_name, common_profiles_dir))
401
-
402
- parent_spec_path = common_profiles_dir.joinpath(
403
- parent_profile_name + CONFIG.get('profile_ext'))
404
-
405
- with parent_spec_path.open() as handle:
406
- parent_profile = yaml.safe_load(handle)
407
- all_profiles.append(parent_profile)
408
-
409
- # Merge inherited profile data:
410
- merged_profile = {}
411
- merged_vars = {}
412
- merged_opts = {}
413
- for i in all_profiles[::-1]:
414
-
415
- # Merge variables and opts independently to allow variables to inherit:
416
- if 'variables' in i:
417
- merged_vars.update(i.pop('variables'))
418
-
419
- if 'scheduler_options' in i:
420
- merged_opts.update(i.pop('scheduler_options'))
421
-
422
- merged_profile.update(**i)
423
-
424
- if 'inherits' in merged_profile:
425
- merged_profile.pop('inherits')
426
-
427
- merged_profile['variables'] = merged_vars
428
- merged_profile['scheduler_options'] = merged_opts
429
-
430
- # Add variables embedded in file name:
431
- allowed_filename_vars = ['profile_order', 'profile_name']
432
- for i in allowed_filename_vars:
433
- if i in filename_var_values:
434
- if i in merged_profile:
435
- msg = ('`{}` is set in the job profile file name and also in '
436
- 'the file YAML contents for job profile file "{}". It '
437
- 'must be specified only once.')
438
- raise ValueError(msg.format(i, job_profile_path.name))
439
- else:
440
- merged_profile.update({i: filename_var_values[i]})
441
-
442
- merged_profile = validate_job_profile(merged_profile)
443
-
444
- cmd_groups = merged_profile['command_groups']
445
- if not cmd_groups:
446
- msg = ('At least one command group must be specified in the job '
447
- 'profile file named "{}".')
448
- raise ValueError(msg.format(job_profile_path.name))
449
-
450
- com_err_msg = '[Job profile name: "{}"]'.format(job_profile_path.name)
451
- cmd_groups = validate_task_multiplicity(cmd_groups, com_err_msg)
452
- merged_profile['command_groups'] = cmd_groups
453
- merged_profile['profile_file'] = job_profile_path
454
-
455
- return merged_profile
hpcflow/project.py DELETED
@@ -1,81 +0,0 @@
1
- """`hpcflow.project.py`
2
-
3
- This module contains a class to represent the root directory of an hpcflow
4
- project.
5
-
6
- """
7
-
8
- import os
9
- import shutil
10
- from pathlib import Path
11
- from subprocess import run
12
-
13
- from hpcflow.config import Config
14
- from hpcflow.utils import get_random_hex
15
-
16
-
17
- class Project(object):
18
-
19
- def __init__(self, dir_path, config_dir=None, clean=False):
20
-
21
- Config.set_config(config_dir)
22
- self.dir_path = Path(dir_path or '').resolve()
23
- self.hf_dir = self.dir_path.joinpath(Config.get('hpcflow_directory'))
24
-
25
- if clean:
26
- self.clean()
27
-
28
- if not self.hf_dir.exists():
29
-
30
- self.db_directory_name = get_random_hex(10)
31
- self.db_path = self.project_db_dir.joinpath(Config.get('DB_name')).as_posix()
32
-
33
- self.hf_dir.mkdir()
34
- self.project_db_dir.mkdir()
35
-
36
- with self.hf_dir.joinpath('db_path').open('w') as handle:
37
- handle.write(self.db_path)
38
-
39
- else:
40
-
41
- with self.hf_dir.joinpath('db_path').open() as handle:
42
- self.db_path = handle.read().strip()
43
- self.db_directory_name = Path(
44
- self.db_path).relative_to(
45
- Config.get('projects_DB_dir')).parent
46
-
47
- @property
48
- def db_uri(self):
49
- return 'sqlite:///' + self.db_path
50
-
51
- @property
52
- def project_db_dir(self):
53
- return Config.get('projects_DB_dir').joinpath(self.db_directory_name)
54
-
55
- @property
56
- def db_dir_symlink(self):
57
- return self.hf_dir.joinpath(self.db_directory_name)
58
-
59
- def ensure_db_symlink(self):
60
- """Add a symlink to the DB for convenience (has to be done after the DB is created)"""
61
-
62
- if not self.db_dir_symlink.exists():
63
- target = str(self.project_db_dir)
64
- link = str(self.db_dir_symlink)
65
- if os.name == 'nt':
66
- cmd = 'mklink /D "{}" "{}"'.format(link, target)
67
- elif os.name == 'posix':
68
- cmd = 'ln --symbolic "{}" "{}"'.format(target, link)
69
-
70
- run(cmd, shell=True)
71
-
72
- def clean(self):
73
- """Remove all hpcflow related files and directories associated with this project."""
74
-
75
- if self.hf_dir.exists():
76
-
77
- # Remove database:
78
- shutil.rmtree(str(self.project_db_dir))
79
-
80
- # Remove project directory:
81
- shutil.rmtree(str(self.hf_dir))