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
hpcflow/variables.py DELETED
@@ -1,544 +0,0 @@
1
- """`hpcflow.variables.py`"""
2
-
3
- import re
4
- from copy import deepcopy
5
- from pprint import pprint
6
- from pathlib import Path
7
-
8
- from hpcflow.config import Config as CONFIG
9
- from hpcflow.utils import coerce_same_length
10
-
11
-
12
- class UnresolvedVariableError(Exception):
13
- pass
14
-
15
-
16
- def get_var_defn_from_template(template, arg):
17
- """Construct a variable from a template and an argument.
18
-
19
- Parameters
20
- ----------
21
- template : dict
22
- A variable definition template from the variable definition lookup
23
- file.
24
- arg : str
25
- A parameter for the template. Where "<<arg>>" appears in the
26
- template `value` or `file_regex.pattern` keys, it is substituted by
27
- this parameter.
28
-
29
- Returns
30
- -------
31
- var_defn : dict
32
- Variable definition dictionary.
33
-
34
- """
35
-
36
- # Replace any instance of `<<arg>>` with the passed argument.
37
- var_defn = deepcopy(template)
38
- regex_pat = var_defn['file_regex']['pattern']
39
- value = var_defn['value']
40
- arg = re.escape(arg)
41
-
42
- if '<<arg>>' in regex_pat:
43
- var_defn['file_regex']['pattern'] = regex_pat.replace('<<arg>>', arg)
44
-
45
- if '<<arg>>' in value:
46
- var_defn['value'] = value.replace('<<arg>>', arg)
47
-
48
- return var_defn
49
-
50
-
51
- def get_all_var_defns_from_lookup(scope):
52
- """Get all in-scope variable definitions from the lookup file.
53
-
54
- Parameters
55
- ----------
56
- scope : str
57
- Scope within the variable lookup file in which the variable definition
58
- resides.
59
-
60
- Returns
61
- -------
62
- var_defns : dict of dict
63
- dict of variable definitions.
64
-
65
- """
66
-
67
- VARS_LOOKUP = CONFIG.get('variable_lookup')
68
- def_scope = VARS_LOOKUP['scopes'].get('_default', {})
69
- all_scoped_vars = VARS_LOOKUP['scopes'].get(scope, def_scope)
70
- scoped_vars = all_scoped_vars.get('variables', {})
71
- scoped_vars_template = all_scoped_vars.get('variables_from_template', {})
72
-
73
- var_defns = deepcopy(scoped_vars)
74
-
75
- for var_name, template_params in scoped_vars_template.items():
76
- # Construct the variable definition from a template
77
- template_name, template_arg = template_params
78
- template = VARS_LOOKUP['variable_templates'][template_name]
79
- var_defn = get_var_defn_from_template(template, template_arg)
80
- var_defns.update({
81
- var_name: var_defn
82
- })
83
-
84
- return var_defns
85
-
86
-
87
- def resolve_sub_vars(var_defns, all_var_defns):
88
- """For all variable names embedded within variable definitions, resolve
89
- their own definitions.
90
-
91
- Variable definitions may include references to other variables. This
92
- function finds these references and returns a set of variable definitions
93
- that resolve these dependencies.
94
-
95
- Parameters
96
- ----------
97
- var_defns : dict
98
- The original set of variable definitions that may include references
99
- to variables within their values.
100
- all_var_defns : dict
101
- A large set of variable definitions from which new definitions are to
102
- be found.
103
-
104
- Returns
105
- -------
106
- new_var_defns : dict
107
- The set of variable definitions that resolve the embedded sub-variable
108
- names in the original set of variable definitions, `var_defns`.
109
-
110
- """
111
-
112
- new_var_defns = {}
113
- var_delims = CONFIG.get('variable_delimiters')
114
-
115
- for _, var_defn_i in var_defns.items():
116
-
117
- val_i = var_defn_i.get('value', '{}')
118
- sub_var_names = extract_variable_names(val_i, var_delims)
119
-
120
- if sub_var_names:
121
- sub_var_defns = {
122
- i: all_var_defns[i]
123
- for i in sub_var_names
124
- }
125
- new_var_defns.update(sub_var_defns)
126
- new_var_defns.update(
127
- resolve_sub_vars(sub_var_defns, all_var_defns)
128
- )
129
-
130
- return new_var_defns
131
-
132
-
133
- def select_cmd_group_var_names(commands, directory):
134
-
135
- var_delims = CONFIG.get('variable_delimiters')
136
-
137
- # Retrieve the names of variables in `commands` and in `directory`:
138
- var_names = []
139
- for cmd in commands:
140
- vars_i = extract_variable_names(cmd, var_delims)
141
- var_names.extend(vars_i)
142
-
143
- if directory:
144
- var_names.extend(extract_variable_names(directory, var_delims))
145
-
146
- # Eliminate duplicates:
147
- var_names = list(set(var_names))
148
-
149
- return var_names
150
-
151
-
152
- def select_cmd_group_var_definitions(var_defns_all, commands, directory):
153
- """For a given command group, select from a list of variable definitions
154
- only those definitions that are required.
155
-
156
- Parameters
157
- ----------
158
- var_defns_all : list of dict
159
- commands : list of str
160
- directory : str
161
-
162
- Returns
163
- -------
164
- var_defns : list of dict
165
- The subset of the input variable definitions that are required for
166
- the commands and directory associated with this command group.
167
-
168
- """
169
-
170
- var_defns = {}
171
- var_names = select_cmd_group_var_names(commands, directory)
172
-
173
- # Add the definitions of found variable names:
174
- for i in var_names:
175
- # TODO handle exception
176
- var_defns.update({i: var_defns_all[i]})
177
-
178
- # Recursively search for, and add definitions of, sub-variables:
179
- sub_var_defns = resolve_sub_vars(var_defns, var_defns_all)
180
- var_defns.update(sub_var_defns)
181
-
182
- return var_defns
183
-
184
-
185
- def extract_variable_names(source_str, delimiters, characters=None):
186
- """Given a specified syntax for embedding variable names within a string,
187
- extract all variable names.
188
-
189
- Parameters
190
- ----------
191
- source_str : str
192
- The string within which to search for variable names.
193
- delimiters : two-tuple of str
194
- The left and right delimiters of a variable name.
195
- characters : str
196
- The regular expression that matches the allowed variable name. By
197
- default, this is set to a regular expression that matches at least one
198
- character.
199
-
200
- Returns
201
- -------
202
- var_names : list of str
203
- The variable names embedded in the original string.
204
-
205
- Examples
206
- --------
207
- Using single parentheses to delimit variable names, where variable names
208
- are formed of at least one Latin letter (including upper- and lower-case):
209
-
210
- >>> source_str = r'(foo).(bar).yml'
211
- >>> delimiters = ('(', ')')
212
- >>> characters = '[a-zA-Z]+?'
213
- >>> extract_variable_names(source_str, delimiters, characters)
214
- ['foo', 'bar']
215
-
216
- Using double angled brackets to delimit variable names, where variable
217
- names are formed of at least one character:
218
-
219
- >>> source_str = r'<<Foo79_8>>.<<baR-baR>>.yml'
220
- >>> delimiters = ('<<', '>>')
221
- >>> characters = '.+?'
222
- >>> extract_variable_names(source_str, delimiters, characters)
223
- ['Foo79_8', 'baR-baR']
224
-
225
- """
226
-
227
- if not characters:
228
- characters = r'.\S+?'
229
-
230
- if not characters.endswith('?'):
231
- # Always match as few characters as possible.
232
- characters += '?'
233
-
234
- delim_esc = [re.escape(i) for i in delimiters]
235
-
236
- # Form a capture group around the variable name:
237
- pattern = delim_esc[0] + '(' + characters + ')' + delim_esc[1]
238
- var_names = re.findall(pattern, source_str)
239
-
240
- return var_names
241
-
242
-
243
- def get_variabled_filename_regex(filename_format, var_delims, var_values,
244
- var_chars=None):
245
- """Get the regular expression that matches a filename that may include
246
- embedded variable values according to a particular syntax.
247
-
248
- Parameters
249
- ----------
250
- filename_format : str
251
- The format of the filenames to match, which can include embedded
252
- variable names, whose syntax is determined by `var_delims`, `var_chars`
253
- and `var_values`.
254
- var_delims : two-tuple of str
255
- The left and right delimiters of variable names as embedded in
256
- `filename_format`.
257
- var_values : dict of (str: str)
258
- Dictionary whose keys are variable names that may appear in
259
- `filename_format`, and whose values are the regular expressions that
260
- matches the variable value.
261
- var_chars : str, optional
262
- The regular expression that matches the allowed variable name. By
263
- default, this is set to `None`, meaning no restriction is placed on the
264
- allowed variable names. If this is set to a string, the keys in
265
- `var_values` must match this regular expression.
266
-
267
- Returns
268
- -------
269
- tuple
270
- filename_regex : str
271
- The regular expression that will match filenames with embedded
272
- variables as determined by the input parameters.
273
- var_names : list
274
- The ordered list of variable names that were matched. The order
275
- matches the parenthesised match groups in `filename_regex`.
276
-
277
- Examples
278
- --------
279
- >>> filename_fmt = '<<order>>.<<foo>>.yml'
280
- >>> var_delims = ['<<', '>>']
281
- >>> var_values = {'foo': r'[a-z]*', 'order': 'r[0-9]+'}
282
- >>> get_variabled_filename_regex(filename_fmt, var_delims, var_values)
283
- ('(r[0-9]+)\\.([a-z]*)\\.yml', ['order', 'foo'])
284
-
285
- """
286
-
287
- if var_chars is not None:
288
- # Check keys in `var_values` match `var_chars` regex:
289
-
290
- var_chars_test = var_chars
291
- if var_chars.endswith('?'):
292
- # Remove "match as few as possible" symbol:
293
- var_chars_test = var_chars_test[:-1]
294
-
295
- for i in var_values:
296
- var_name_match = re.match(var_chars_test, i)
297
- if not var_name_match or var_name_match.group() != i:
298
- msg = ('`var_chars` regex given as "{}" does not match with '
299
- 'the variable name: "{}"')
300
- raise ValueError(msg.format(var_chars, i))
301
-
302
- # Extract the variable names from the profile filename format:
303
- var_names = extract_variable_names(filename_format, var_delims, var_chars)
304
-
305
- # If there are no variable names, just do regex escape on it, and return:
306
- if not var_names:
307
- filename_format_esc = re.escape(filename_format)
308
- var_names = []
309
- return (filename_format_esc, var_names)
310
-
311
- # Count how many times each variable appears in `pattern`:
312
- var_count = {}
313
- for i in var_names:
314
- if i in var_count:
315
- var_count[i] += 1
316
- else:
317
- var_count[i] = 1
318
-
319
- tot_var_count = sum(var_count.values())
320
-
321
- # Generate regex for matching the variable names in `pattern`:
322
- var_delims_esc = [re.escape(i) for i in var_delims]
323
- var_placeholder_esc = '{}{{}}{}'.format(*var_delims_esc)
324
- vars_fmt = '|'.join([var_placeholder_esc.format(i) for i in var_values])
325
- vars_fmt = '(' + vars_fmt + ')'
326
-
327
- # Need to regex-escape everything in `pattern` that is not a variable:
328
- spec_fmt_match = r'(.*)'.join(['{0:}' for _ in range(tot_var_count)])
329
- spec_fmt_match = r'(.*)' + spec_fmt_match.format(vars_fmt) + r'(.*)'
330
-
331
- match = list(re.match(spec_fmt_match, filename_format).groups())
332
- for i in range(0, len(match), 2):
333
- match[i] = re.escape(match[i])
334
-
335
- filename_regex = ''.join(match)
336
-
337
- # Replace variable placeholders with regex:
338
- var_placeholder = '{}{{}}{}'.format(*var_delims)
339
- for k, v in var_values.items():
340
- filename_regex = filename_regex.replace(
341
- var_placeholder.format(k), r'({})'.format(v))
342
-
343
- return filename_regex, var_names
344
-
345
-
346
- def find_variabled_filenames(file_paths, filename_regex, var_names, var_types,
347
- all_must_match=True, check_exists=True):
348
- """Find which in a list of file names match a regular expression, and
349
- capture parenthesised match groups for each filename match.
350
-
351
- Parameters
352
- ----------
353
- file_paths : list of (str or Path)
354
- Path of files whose names are to be matched against a given regular
355
- expression.
356
- filename_regex : str
357
- Regular expression to match filenames to.
358
- var_names : list
359
- List of variable names whose order matches the parenthesised match
360
- groups in `filename_regex`.
361
- var_types : dict of (str : type)
362
- Expected Python type of each variable value to which the regex matches
363
- will be cast.
364
-
365
- Returns
366
- -------
367
- file_matches : dict of (Path : dict)
368
- Dictionary whose keys are Path objects that point to matched files.
369
- Values are dictionaries that map the variable name to its value for
370
- that matched file.
371
-
372
- Examples
373
- --------
374
-
375
- TODO redo example.
376
-
377
- # If the current working directory (invoking directory) contains the files
378
- # "1.run.yml" and "2.process.yml", then the following can be evaluated:
379
-
380
- # >>> fn_regex = r'([0-9]+)\.([a-z]*)\.yml'
381
- # >>> var_names = ['order', 'foo']
382
- # >>> var_types = {'foo': str, 'order': int}
383
- # >>> find_variabled_filenames('', fn_regex, var_names, var_types)
384
- # {WindowsPath('1.run.yml'): {'order': 1, 'foo': 'run'},
385
- # WindowsPath('2.process.yml'): {'order': 2, 'foo': 'process'}}
386
-
387
- """
388
-
389
- file_matches = {}
390
-
391
- for i in file_paths:
392
-
393
- i = Path(i)
394
- if check_exists:
395
- if not i.exists():
396
- msg = ('File named "{}" does not exist, but parameter '
397
- '`check_exists=True`.')
398
- raise FileNotFoundError(msg.format(i.name))
399
-
400
- i_match = re.fullmatch(filename_regex, i.name)
401
-
402
- if i_match:
403
- match_groups = i_match.groups()
404
- vars_i = {}
405
- for var_idx, var_name in enumerate(var_names):
406
- var_val = match_groups[var_idx]
407
- var_val_cast = var_types[var_name](var_val)
408
-
409
- if var_name in vars_i and var_val_cast != vars_i[var_name]:
410
- msg = ('Inconsistent values for variable: "{}". Values are'
411
- ' "{}" and "{}".')
412
- raise ValueError(msg.format(
413
- var_name, vars_i[var_name], var_val_cast))
414
-
415
- vars_i.update({var_name: var_val_cast})
416
-
417
- file_matches.update({i: vars_i})
418
-
419
- elif all_must_match:
420
- msg = ('File named "{}" does not match the regular expression "{}"'
421
- ' but parameter `all_must_match=True`.')
422
- raise ValueError(msg.format(i.name, filename_regex))
423
-
424
- return file_matches
425
-
426
-
427
- def resolve_variable_values(var_defns, directory):
428
- """Get the values of variables.
429
-
430
- Parameters
431
- ----------
432
- var_defns : list of VarDefinition
433
- directory : Path
434
- Directory within which to resolve the values.
435
-
436
- """
437
-
438
- unresolvable_var_names = []
439
- var_vals = {}
440
- dep_map = {}
441
-
442
- for i in var_defns:
443
-
444
- if i.is_base_variable():
445
-
446
- try:
447
- vals = i.get_values(directory)
448
- except UnresolvedVariableError:
449
- unresolvable_var_names.append(i.name)
450
- continue
451
-
452
- var_vals.update({
453
- i.name: {
454
- 'vals': vals,
455
- 'sub_var_vals': {},
456
- }
457
- })
458
-
459
- else:
460
- dep_map.update({
461
- i.name: i.get_dependent_variable_names(),
462
- })
463
-
464
- # Remove all vars from dep_map that depend on unresolvable vars:
465
- dep_map_new = {}
466
- for k, v in dep_map.items():
467
-
468
- keep_dep = True
469
- for i in v:
470
- if i in unresolvable_var_names:
471
- keep_dep = False
472
- break
473
-
474
- if keep_dep:
475
- dep_map_new.update({k: v})
476
-
477
- dep_map = dep_map_new
478
-
479
- count = 0
480
- while dep_map:
481
-
482
- if count > 5:
483
- msg = 'Could not resolve variables in 10 iterations.'
484
- raise ValueError(msg)
485
-
486
- count += 1
487
- dep_map_new = {}
488
-
489
- for k, v in dep_map.items():
490
-
491
- sub_var_vals = {}
492
- dep_resolved = True
493
-
494
- for i in v:
495
-
496
- if i in var_vals:
497
- sub_var_vals.update({i: var_vals[i]['vals']})
498
-
499
- else:
500
- dep_resolved = False
501
- break
502
-
503
- if dep_resolved:
504
-
505
- var_defn_i = [i for i in var_defns if i.name == k][0]
506
- values = var_defn_i.get_values(directory)
507
- err_msg = 'Variable multiplicity mismatch!'
508
-
509
- sub_var_vals_keys = list(sub_var_vals.keys())
510
- sub_var_vals_vals = list(sub_var_vals.values())
511
-
512
- # Coerce all sub_var_vals and values to have the same length:
513
- try:
514
- coerced_vals = coerce_same_length(
515
- [values] + sub_var_vals_vals)
516
- except ValueError:
517
- raise ValueError(err_msg)
518
-
519
- vals_new = coerced_vals[0]
520
- sub_var_vals_new = dict(zip(sub_var_vals_keys,
521
- coerced_vals[1:]))
522
-
523
- for k2, v2 in sub_var_vals_new.items():
524
-
525
- for idx, rep in enumerate(v2):
526
-
527
- vals_new[idx] = vals_new[idx].replace(
528
- '<<{}>>'.format(k2), rep)
529
-
530
- var_vals.update({
531
- k: {
532
- 'vals': vals_new,
533
- 'sub_var_vals': sub_var_vals,
534
- }
535
- })
536
-
537
- else:
538
- dep_map_new.update({
539
- k: v
540
- })
541
-
542
- dep_map = dep_map_new
543
-
544
- return var_vals