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.
- hpcflow/__init__.py +2 -11
- hpcflow/__pyinstaller/__init__.py +5 -0
- hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
- hpcflow/_version.py +1 -1
- hpcflow/app.py +43 -0
- hpcflow/cli.py +2 -462
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
- hpcflow/data/jinja_templates/test/test_template.txt +8 -0
- hpcflow/data/programs/hello_world/README.md +1 -0
- hpcflow/data/programs/hello_world/hello_world.c +87 -0
- hpcflow/data/programs/hello_world/linux/hello_world +0 -0
- hpcflow/data/programs/hello_world/macos/hello_world +0 -0
- hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
- hpcflow/data/scripts/__init__.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
- hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/generate_t1_file_01.py +7 -0
- hpcflow/data/scripts/import_future_script.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
- hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
- hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/parse_t1_file_01.py +4 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/__init__.py +1 -0
- hpcflow/data/template_components/command_files.yaml +26 -0
- hpcflow/data/template_components/environments.yaml +13 -0
- hpcflow/data/template_components/parameters.yaml +14 -0
- hpcflow/data/template_components/task_schemas.yaml +139 -0
- hpcflow/data/workflows/workflow_1.yaml +5 -0
- hpcflow/examples.ipynb +1037 -0
- hpcflow/sdk/__init__.py +149 -0
- hpcflow/sdk/app.py +4266 -0
- hpcflow/sdk/cli.py +1479 -0
- hpcflow/sdk/cli_common.py +385 -0
- hpcflow/sdk/config/__init__.py +5 -0
- hpcflow/sdk/config/callbacks.py +246 -0
- hpcflow/sdk/config/cli.py +388 -0
- hpcflow/sdk/config/config.py +1410 -0
- hpcflow/sdk/config/config_file.py +501 -0
- hpcflow/sdk/config/errors.py +272 -0
- hpcflow/sdk/config/types.py +150 -0
- hpcflow/sdk/core/__init__.py +38 -0
- hpcflow/sdk/core/actions.py +3857 -0
- hpcflow/sdk/core/app_aware.py +25 -0
- hpcflow/sdk/core/cache.py +224 -0
- hpcflow/sdk/core/command_files.py +814 -0
- hpcflow/sdk/core/commands.py +424 -0
- hpcflow/sdk/core/element.py +2071 -0
- hpcflow/sdk/core/enums.py +221 -0
- hpcflow/sdk/core/environment.py +256 -0
- hpcflow/sdk/core/errors.py +1043 -0
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +809 -0
- hpcflow/sdk/core/loop.py +1320 -0
- hpcflow/sdk/core/loop_cache.py +282 -0
- hpcflow/sdk/core/object_list.py +933 -0
- hpcflow/sdk/core/parameters.py +3371 -0
- hpcflow/sdk/core/rule.py +196 -0
- hpcflow/sdk/core/run_dir_files.py +57 -0
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +3792 -0
- hpcflow/sdk/core/task_schema.py +993 -0
- hpcflow/sdk/core/test_utils.py +538 -0
- hpcflow/sdk/core/types.py +447 -0
- hpcflow/sdk/core/utils.py +1207 -0
- hpcflow/sdk/core/validation.py +87 -0
- hpcflow/sdk/core/values.py +477 -0
- hpcflow/sdk/core/workflow.py +4820 -0
- hpcflow/sdk/core/zarr_io.py +206 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/data/config_file_schema.yaml +34 -0
- hpcflow/sdk/data/config_schema.yaml +260 -0
- hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
- hpcflow/sdk/data/files_spec_schema.yaml +5 -0
- hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
- hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
- hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/demo/cli.py +242 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +137 -0
- hpcflow/sdk/helper/helper.py +300 -0
- hpcflow/sdk/helper/watcher.py +192 -0
- hpcflow/sdk/log.py +288 -0
- hpcflow/sdk/persistence/__init__.py +18 -0
- hpcflow/sdk/persistence/base.py +2817 -0
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +39 -0
- hpcflow/sdk/persistence/json.py +954 -0
- hpcflow/sdk/persistence/pending.py +948 -0
- hpcflow/sdk/persistence/store_resource.py +203 -0
- hpcflow/sdk/persistence/types.py +309 -0
- hpcflow/sdk/persistence/utils.py +73 -0
- hpcflow/sdk/persistence/zarr.py +2388 -0
- hpcflow/sdk/runtime.py +320 -0
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/enums.py +70 -0
- hpcflow/sdk/submission/jobscript.py +2379 -0
- hpcflow/sdk/submission/schedulers/__init__.py +281 -0
- hpcflow/sdk/submission/schedulers/direct.py +233 -0
- hpcflow/sdk/submission/schedulers/sge.py +376 -0
- hpcflow/sdk/submission/schedulers/slurm.py +598 -0
- hpcflow/sdk/submission/schedulers/utils.py +25 -0
- hpcflow/sdk/submission/shells/__init__.py +52 -0
- hpcflow/sdk/submission/shells/base.py +229 -0
- hpcflow/sdk/submission/shells/bash.py +504 -0
- hpcflow/sdk/submission/shells/os_version.py +115 -0
- hpcflow/sdk/submission/shells/powershell.py +352 -0
- hpcflow/sdk/submission/submission.py +1402 -0
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +194 -0
- hpcflow/sdk/utils/arrays.py +69 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +31 -0
- hpcflow/sdk/utils/strings.py +69 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +123 -0
- hpcflow/tests/data/__init__.py +0 -0
- hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
- hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_1.json +10 -0
- hpcflow/tests/data/workflow_1.yaml +5 -0
- hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
- hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
- hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
- hpcflow/tests/programs/test_programs.py +180 -0
- hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +1361 -0
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
- hpcflow/tests/unit/test_action.py +1066 -0
- hpcflow/tests/unit/test_action_rule.py +24 -0
- hpcflow/tests/unit/test_app.py +132 -0
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +172 -0
- hpcflow/tests/unit/test_command.py +377 -0
- hpcflow/tests/unit/test_config.py +195 -0
- hpcflow/tests/unit/test_config_file.py +162 -0
- hpcflow/tests/unit/test_element.py +666 -0
- hpcflow/tests/unit/test_element_iteration.py +88 -0
- hpcflow/tests/unit/test_element_set.py +158 -0
- hpcflow/tests/unit/test_group.py +115 -0
- hpcflow/tests/unit/test_input_source.py +1479 -0
- hpcflow/tests/unit/test_input_value.py +398 -0
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +1247 -0
- hpcflow/tests/unit/test_loop.py +2674 -0
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
- hpcflow/tests/unit/test_object_list.py +116 -0
- hpcflow/tests/unit/test_parameter.py +243 -0
- hpcflow/tests/unit/test_persistence.py +664 -0
- hpcflow/tests/unit/test_resources.py +243 -0
- hpcflow/tests/unit/test_run.py +286 -0
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +9 -0
- hpcflow/tests/unit/test_schema_input.py +372 -0
- hpcflow/tests/unit/test_shell.py +129 -0
- hpcflow/tests/unit/test_slurm.py +39 -0
- hpcflow/tests/unit/test_submission.py +502 -0
- hpcflow/tests/unit/test_task.py +2560 -0
- hpcflow/tests/unit/test_task_schema.py +182 -0
- hpcflow/tests/unit/test_utils.py +616 -0
- hpcflow/tests/unit/test_value_sequence.py +549 -0
- hpcflow/tests/unit/test_values.py +91 -0
- hpcflow/tests/unit/test_workflow.py +827 -0
- hpcflow/tests/unit/test_workflow_template.py +186 -0
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/unit/utils/test_strings.py +97 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +355 -0
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +564 -0
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6794 -0
- hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
- hpcflow-0.2.0a271.dist-info/METADATA +65 -0
- hpcflow-0.2.0a271.dist-info/RECORD +237 -0
- {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
- hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
- hpcflow/api.py +0 -458
- hpcflow/archive/archive.py +0 -308
- hpcflow/archive/cloud/cloud.py +0 -47
- hpcflow/archive/cloud/errors.py +0 -9
- hpcflow/archive/cloud/providers/dropbox.py +0 -432
- hpcflow/archive/errors.py +0 -5
- hpcflow/base_db.py +0 -4
- hpcflow/config.py +0 -232
- hpcflow/copytree.py +0 -66
- hpcflow/data/examples/_config.yml +0 -14
- hpcflow/data/examples/damask/demo/1.run.yml +0 -4
- hpcflow/data/examples/damask/demo/2.process.yml +0 -29
- hpcflow/data/examples/damask/demo/geom.geom +0 -2052
- hpcflow/data/examples/damask/demo/load.load +0 -1
- hpcflow/data/examples/damask/demo/material.config +0 -185
- hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
- hpcflow/data/examples/damask/inputs/load.load +0 -1
- hpcflow/data/examples/damask/inputs/material.config +0 -185
- hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
- hpcflow/data/examples/damask/profiles/damask.yml +0 -4
- hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
- hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
- hpcflow/data/examples/damask/profiles/default.yml +0 -6
- hpcflow/data/examples/thinking.yml +0 -177
- hpcflow/errors.py +0 -2
- hpcflow/init_db.py +0 -37
- hpcflow/models.py +0 -2549
- hpcflow/nesting.py +0 -9
- hpcflow/profiles.py +0 -455
- hpcflow/project.py +0 -81
- hpcflow/scheduler.py +0 -323
- hpcflow/utils.py +0 -103
- hpcflow/validation.py +0 -167
- hpcflow/variables.py +0 -544
- hpcflow-0.1.9.dist-info/METADATA +0 -168
- hpcflow-0.1.9.dist-info/RECORD +0 -45
- hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
- hpcflow-0.1.9.dist-info/top_level.txt +0 -1
- /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
- /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
- /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
|