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