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
@@ -1,432 +0,0 @@
1
- """`hpcflow.archive.cloud.providers.dropbox.py`
2
-
3
- This module provides interactivity with a Dropbox account for archiving.
4
-
5
- """
6
-
7
- import os
8
- import posixpath
9
- import fnmatch
10
- from pathlib import Path
11
- from pprint import pprint
12
- from datetime import datetime
13
- from textwrap import dedent
14
-
15
- import dropbox as dropbox_api
16
-
17
- from hpcflow.config import Config
18
- from hpcflow.archive.errors import ArchiveError
19
- from hpcflow.archive.cloud.errors import CloudProviderError, CloudCredentialsError
20
-
21
-
22
- def get_token():
23
-
24
- APP_KEY = Config.get('dropbox_app_key')
25
- auth_flow = dropbox_api.DropboxOAuth2FlowNoRedirect(APP_KEY, use_pkce=True)
26
- authorize_url = auth_flow.start()
27
-
28
- msg = dedent(f"""
29
- --------------------------- Connecting hpcflow to Dropbox ----------------------------
30
-
31
- 1. Go to this URL:
32
-
33
- {authorize_url}
34
-
35
- 2. Click "Allow" (you might have to log in first).
36
- 3. Copy the authorization code below.
37
-
38
- --------------------------------------------------------------------------------------
39
- """)
40
- print(msg)
41
-
42
- auth_code = input('Enter the authorization code here: ').strip()
43
- try:
44
- oauth_result = auth_flow.finish(auth_code)
45
- except Exception as err:
46
- print(f'Error: {err}')
47
-
48
- token = oauth_result.access_token
49
-
50
- return token
51
-
52
-
53
- def get_dropbox():
54
- env_var_name = 'DROPBOX_TOKEN'
55
- token = Config.get('dropbox_token') or os.getenv(env_var_name)
56
- if not token:
57
- msg = ('Please set the Dropbox access token in an environment variable '
58
- f'"{env_var_name}", or in the config file as "dropbox_token".')
59
- raise CloudCredentialsError(msg)
60
-
61
- dbx = dropbox_api.Dropbox(token)
62
-
63
- return dbx
64
-
65
-
66
- def check_access():
67
- dbx = get_dropbox()
68
- try:
69
- user_acc = dbx.users_get_current_account()
70
- except:
71
- msg = 'Could not connect to Dropbox using the supplied access token.'
72
- raise ValueError(msg)
73
-
74
- print('OK!', flush=True)
75
-
76
-
77
- def get_files(dbx, path):
78
- path = normalise_path(path)
79
- out = []
80
- for i in dbx.files_list_folder(path).entries:
81
- if isinstance(i, dropbox_api.files.FileMetadata):
82
- out.append(i.name)
83
- return out
84
-
85
-
86
- def get_folders(dbx, path):
87
- path = normalise_path(path)
88
- out = []
89
- for i in dbx.files_list_folder(path).entries:
90
- if isinstance(i, dropbox_api.files.FolderMetadata):
91
- out.append(i.name)
92
- return out
93
-
94
-
95
- def is_file(dbx, path):
96
- """Check given path on dropbox is a file."""
97
- meta = dbx.files_get_metadata(path)
98
- return isinstance(meta, dropbox_api.files.FileMetadata)
99
-
100
-
101
- def is_folder(dbx, path):
102
- """Check given path on dropbox is a folder."""
103
- try:
104
- meta = dbx.files_get_metadata(path)
105
- return isinstance(meta, dropbox_api.files.FolderMetadata)
106
- except dropbox_api.exceptions.ApiError:
107
- return False
108
-
109
-
110
- def rename_file(dbx, src_path, dst_path):
111
- """Rename a file from one path to another."""
112
- if is_file(dbx, src_path):
113
- print('Renaming file: {} to {}'.format(src_path, dst_path))
114
- dbx.files_move_v2(src_path, dst_path)
115
- else:
116
- raise ValueError('Cannot rename a file that does not exist.')
117
-
118
-
119
- def download_dropbox_file(dbx, dropbox_path, local_path):
120
- dbx.files_download_to_file(local_path, dropbox_path)
121
-
122
-
123
- def normalise_path(path):
124
- 'Modify a path (str or Path) such that it is a Dropbox-compatible path string.'
125
- path = posixpath.join(*str(path).split(os.path.sep))
126
- if not path.startswith('/'):
127
- path = '/' + path
128
- return path
129
-
130
-
131
- def archive_file(dbx, local_path, dropbox_dir, check_modified_time=True):
132
- """Upload a file to a dropbox directory such that if the local file is newer than the
133
- copy on Dropbox, the newer file overwrites the older file, and if the local file is
134
- older than the copy on Dropbox, the local file is uploaded with an "auto-incremented"
135
- name.
136
-
137
- Note that if the modified times are different, the file still won't be uploaded
138
- by dropbox if the file contents are identical. The point of checking for the
139
- modified times is to save some time by not having Dropbox check file contents.
140
-
141
- Parameters
142
- ----------
143
- dbx: Dropbox
144
- local_path : str or Path
145
- Path of file on local computer to upload to dropbox.
146
- dropbox_dir : str or Path
147
- Directory on Dropbox into which the file should be uploaded.
148
-
149
- """
150
-
151
- local_path = Path(local_path)
152
- dropbox_path = normalise_path(Path(dropbox_dir).joinpath(local_path.name))
153
-
154
- if not check_modified_time:
155
- overwrite = True
156
- autorename = False
157
- client_modified = None
158
-
159
- else:
160
- # Note: Dropbox culls microseconds from the datetime passed as the
161
- # `client_modified` parameter (it does not round).
162
- # Ref: ("https://www.dropboxforum.com/t5/API-Support-Feedback/Python-API-client"
163
- # "-modified-resolution/m-p/362170/highlight/true#M20596")
164
- ts_sec = local_path.stat().st_mtime_ns / 1e9
165
- dt = datetime.utcfromtimestamp(ts_sec)
166
- client_modified = datetime( # no microseconds
167
- year=dt.year,
168
- month=dt.month,
169
- day=dt.day,
170
- hour=dt.hour,
171
- minute=dt.minute,
172
- second=dt.second,
173
- )
174
-
175
- try:
176
- # Check for existing file
177
- existing_file = dbx.files_get_metadata(dropbox_path)
178
- existing_modified = existing_file.client_modified
179
-
180
- # print('client_modified: {!r}'.format(client_modified))
181
- # print('existing_modified: {!r}'.format(existing_modified))
182
-
183
- if client_modified == existing_modified:
184
- # print('client_modified time same!')
185
- return
186
-
187
- elif client_modified < existing_modified:
188
- overwrite = False
189
- autorename = True
190
- # print('client file older!')
191
-
192
- elif client_modified > existing_modified:
193
- overwrite = True
194
- autorename = False
195
- # print('client file newer!')
196
-
197
- except dropbox_api.exceptions.ApiError:
198
-
199
- # print('File does not exist.')
200
-
201
- overwrite = False
202
- autorename = False
203
-
204
- except:
205
- msg = 'Unexpected error.'
206
- raise CloudProviderError(msg)
207
-
208
- _upload_dropbox_file(
209
- dbx,
210
- local_path,
211
- dropbox_path,
212
- overwrite=overwrite,
213
- autorename=autorename,
214
- client_modified=client_modified
215
- )
216
-
217
-
218
- def upload_dropbox_file(dbx, local_path, dropbox_dir, overwrite=False, autorename=True,
219
- client_modified=None):
220
- """
221
- Parameters
222
- ----------
223
- dbx: Dropbox
224
- local_path : str or Path
225
- Path of file on local computer to upload to dropbox.
226
- dropbox_dir : str or Path
227
- Directory on Dropbox into which the file should be uploaded.
228
- overwrite : bool
229
- If True, the file overwrites an existing file with the same name.
230
- autorename : bool
231
- If True, rename the file if there is a conflict.
232
-
233
- """
234
-
235
- local_path = Path(local_path)
236
- dropbox_path = normalise_path(Path(dropbox_dir).joinpath(local_path.name))
237
- _upload_dropbox_file(
238
- dbx,
239
- local_path,
240
- dropbox_path,
241
- overwrite=overwrite,
242
- autorename=autorename,
243
- client_modified=client_modified,
244
- )
245
-
246
-
247
- def _upload_dropbox_file(dbx, local_path, dropbox_path, overwrite=False, autorename=True,
248
- client_modified=None):
249
- """
250
- Parameters
251
- ----------
252
- dbx: Dropbox
253
- local_path : Path
254
- Path of file on local computer to upload to dropbox.
255
- dropbox_path : str
256
- Path to upload the file to nn dropbox.
257
- overwrite : bool
258
- If True, the file overwrites an existing file with the same name.
259
- autorename : bool
260
- If True, rename the file if there is a conflict.
261
-
262
- """
263
-
264
- if overwrite:
265
- mode = dropbox_api.dropbox.files.WriteMode('overwrite', None)
266
- else:
267
- mode = dropbox_api.dropbox.files.WriteMode('add', None)
268
-
269
- try:
270
- with local_path.open(mode='rb') as handle:
271
-
272
- try:
273
- dbx.files_upload(
274
- handle.read(),
275
- dropbox_path,
276
- mode=mode,
277
- autorename=autorename,
278
- client_modified=client_modified,
279
- )
280
-
281
- except dropbox_api.exceptions.ApiError as err:
282
- msg = ('Cloud provider error. {}'.format(err))
283
- raise CloudProviderError(msg)
284
- except:
285
- msg = 'Unexpected error.'
286
- raise CloudProviderError(msg)
287
-
288
- except FileNotFoundError as err:
289
- raise ArchiveError(err)
290
-
291
-
292
- def archive_directory(dbx, local_dir, dropbox_dir, exclude=None):
293
- """
294
- Archive a the contents of a local directory into a directory on dropbox.
295
-
296
- Any files in the dropbox directory not in the source directory are ignored.
297
-
298
- Parameters
299
- ----------
300
- dbx: Dropbox
301
- local_dir : str or Path
302
- Path of directory on local computer to upload to dropbox.
303
- dropbox_dir : str or Path
304
- Directory on dropbox to upload the files to.
305
-
306
- """
307
-
308
- print('hpcflow.archive.cloud.providers.dropbox.archive_directory', flush=True)
309
-
310
- local_dir = Path(local_dir)
311
- dropbox_dir = Path(dropbox_dir)
312
- _upload_dropbox_dir(
313
- dbx,
314
- local_dir,
315
- dropbox_dir,
316
- exclude=exclude,
317
- archive=True,
318
- )
319
-
320
-
321
- def upload_dropbox_dir(dbx, local_dir, dropbox_dir, overwrite=False, autorename=False,
322
- exclude=None):
323
- """
324
- Parameters
325
- ----------
326
- dbx: Dropbox
327
- local_dir : str or Path
328
- Path of directory on local computer to upload to dropbox.
329
- dropbox_dir : str or Path
330
- Directory on dropbox to upload the file to.
331
- overwrite : bool
332
- If True, the file overwrites an existing file with the same name.
333
- autorename : bool
334
- If True, rename the file if there is a conflict.
335
- exclude : list, optional
336
- List of file or directory names to exclude, matched with `fnmatch` for
337
- files, or compared directly for directories.
338
-
339
- """
340
-
341
- local_dir = Path(local_dir)
342
- dropbox_dir = Path(dropbox_dir)
343
- _upload_dropbox_dir(
344
- dbx,
345
- local_dir,
346
- dropbox_dir,
347
- overwrite=overwrite,
348
- autorename=autorename,
349
- exclude=exclude,
350
- archive=False,
351
- )
352
-
353
-
354
- def _upload_dropbox_dir(dbx, local_dir, dropbox_dir, overwrite=False, autorename=False,
355
- exclude=None, archive=False):
356
- """
357
- Parameters
358
- ----------
359
- dbx: Dropbox
360
- local_dir : Path
361
- Path of directory on local computer to upload to dropbox.
362
- dropbox_dir : Path
363
- Directory on dropbox to upload the file to.
364
- overwrite : bool
365
- If True, the file overwrites an existing file with the same name.
366
- autorename : bool
367
- If True, rename the file if there is a conflict.
368
- exclude : list, optional
369
- List of file or directory names to exclude, matched with `fnmatch` for
370
- files, or compared directly for directories.
371
- archive : bool, optional
372
-
373
- Notes
374
- -----
375
- Does not upload empty directories.
376
-
377
- """
378
-
379
- if not exclude:
380
- exclude = []
381
-
382
- # Validation
383
- if not local_dir.is_dir():
384
- raise ValueError('Specified `local_dir` is not a directory: {}'.format(local_dir))
385
-
386
- for root, dirs, files in os.walk(str(local_dir)):
387
-
388
- root_test = Path(root)
389
-
390
- dirs[:] = [d for d in dirs if d not in exclude]
391
-
392
- print('Uploading from root directory: {}'.format(root), flush=True)
393
-
394
- for file_name in sorted(files):
395
-
396
- up_file = False
397
-
398
- if exclude is not None:
399
- if not any([fnmatch.fnmatch(file_name, i) for i in exclude]):
400
- up_file = True
401
- else:
402
- up_file = True
403
-
404
- if up_file:
405
-
406
- src_file = root_test.joinpath(file_name)
407
- rel_path = src_file.relative_to(local_dir)
408
- dst_dir = dropbox_dir.joinpath(rel_path.parent)
409
-
410
- print('Uploading file: {}'.format(file_name), flush=True)
411
- try:
412
- if archive:
413
- archive_file(
414
- dbx,
415
- src_file,
416
- dst_dir
417
- )
418
- else:
419
- upload_dropbox_file(
420
- dbx,
421
- src_file,
422
- dst_dir,
423
- overwrite=overwrite,
424
- autorename=autorename
425
- )
426
- except ArchiveError as err:
427
- print('Archive error: {}'.format(err), flush=True)
428
- continue
429
-
430
- except CloudProviderError as err:
431
- print('Cloud provider error: {}'.format(err), flush=True)
432
- continue
hpcflow/archive/errors.py DELETED
@@ -1,5 +0,0 @@
1
- """`hpcflow.archive.errors.py`"""
2
-
3
-
4
- class ArchiveError(Exception):
5
- pass
hpcflow/base_db.py DELETED
@@ -1,4 +0,0 @@
1
- """`hpcflow.base_db.py`"""
2
-
3
- from sqlalchemy.ext.declarative import declarative_base
4
- Base = declarative_base()
hpcflow/config.py DELETED
@@ -1,232 +0,0 @@
1
- import os
2
- from pathlib import Path
3
- from warnings import warn
4
-
5
- from ruamel.yaml import YAML, safe_load
6
-
7
- from hpcflow.errors import ConfigurationError
8
-
9
-
10
- class Config(object):
11
-
12
- __PROFILE_KEYS_REQ = [
13
- 'command_groups',
14
- 'profile_name',
15
- 'profile_order',
16
- ]
17
- __PROFILE_KEYS_GOOD = __PROFILE_KEYS_REQ + [
18
- 'alternate_scratch',
19
- 'archive_locations',
20
- 'archive',
21
- 'archive_excludes',
22
- 'directory',
23
- 'inherits',
24
- 'is_job_array',
25
- 'loop',
26
- 'environment',
27
- 'nesting',
28
- 'scheduler',
29
- 'scheduler_options',
30
- 'output_dir',
31
- 'error_dir',
32
- 'pre_commands',
33
- 'root_archive',
34
- 'root_archive_excludes',
35
- 'variable_scope',
36
- 'variables',
37
- 'stats',
38
- ]
39
- __CMD_GROUP_KEYS_REQ = [
40
- 'commands',
41
- ]
42
- __CMD_GROUP_KEYS_GOOD = __CMD_GROUP_KEYS_REQ + [
43
- 'alternate_scratch',
44
- 'archive',
45
- 'archive_excludes',
46
- 'directory',
47
- 'is_job_array',
48
- 'environment',
49
- 'nesting',
50
- 'scheduler',
51
- 'scheduler_options',
52
- 'output_dir',
53
- 'error_dir',
54
- 'profile_name',
55
- 'profile_order',
56
- 'exec_order',
57
- 'stats',
58
- 'job_name',
59
- ]
60
- __CMD_GROUP_DEFAULTS = {
61
- 'is_job_array': True,
62
- 'nesting': None,
63
- 'directory': '',
64
- 'archive': None,
65
- 'archive_excludes': [],
66
- 'scheduler': 'direct',
67
- 'scheduler_options': {},
68
- 'output_dir': None, # Set in `Config.set_config`
69
- 'error_dir': None, # Set in `Config.set_config`
70
- 'stats': False,
71
- }
72
-
73
- __CONSTANTS = {
74
- 'DB_name': 'workflow.db',
75
- 'alt_scratch_exc_file': 'alt_scratch_exclude',
76
- 'alt_scratch_exc_file_ext': '.txt',
77
- 'profile_keys_required': __PROFILE_KEYS_REQ,
78
- 'profile_keys_allowed': __PROFILE_KEYS_GOOD,
79
- 'cmd_group_keys_required': __CMD_GROUP_KEYS_REQ,
80
- 'cmd_group_keys_allowed': __CMD_GROUP_KEYS_GOOD,
81
- 'cmd_group_defaults': __CMD_GROUP_DEFAULTS,
82
- 'dropbox_app_key': 'g2zt0hmhfjavd2d',
83
- }
84
-
85
- # These may be customised in the config file:
86
- __ALLOWED = {
87
- 'data_dir': None,
88
- 'variable_delimiters': ['<<', '>>'],
89
- 'default_cmd_group_dir_var_name': '__hpcflow_cmd_group_directory_var',
90
- 'profile_filename_fmt': '<<profile_order>>.<<profile_name>>.yml',
91
- 'profile_ext': '.yml',
92
- 'jobscript_ext': '.sh',
93
- 'variable_file_ext': '.txt',
94
- 'working_dirs_file_ext': '.txt',
95
- 'default_output_dir': 'output',
96
- 'default_error_dir': 'output',
97
- 'hpcflow_directory': '.hpcflow',
98
- 'archive_locations': {},
99
- 'dropbox_token': None
100
- }
101
-
102
- __conf = {}
103
-
104
- is_set = False
105
-
106
- @staticmethod
107
- def resolve_config_dir(config_dir=None):
108
-
109
- if not config_dir:
110
- config_dir = Path(os.getenv('HPCFLOW_CONFIG_DIR', '~/.hpcflow')).expanduser()
111
- else:
112
- config_dir = Path(config_dir)
113
-
114
- if Config.is_set:
115
- if config_dir != Config.get('config_dir'):
116
- warn(f'Config is already set, but `config_dir` changed from '
117
- f'"{Config.get("config_dir")}" to "{config_dir}".')
118
-
119
- if not config_dir.is_dir():
120
- print('Configuration directory does not exist. Generating.')
121
- config_dir.mkdir()
122
-
123
- return config_dir
124
-
125
- @staticmethod
126
- def get_config_file(config_dir, round_trip_load=False, quiet=False):
127
-
128
- config_file = config_dir.joinpath('config.yml')
129
- if not config_file.is_file():
130
- if not quiet:
131
- print('No config.yml found. Generating a config.yml file.')
132
- with config_file.open('w'):
133
- pass
134
-
135
- if not quiet:
136
- print(f'Loading hpcflow config from {config_file}.')
137
- if round_trip_load:
138
- yaml = YAML(typ='rt')
139
- else:
140
- yaml = YAML(typ='safe')
141
- with config_file.open() as handle:
142
- config_dat = yaml.load(handle) or {}
143
-
144
- bad_keys = list(set(config_dat.keys()) - set(Config.__ALLOWED.keys()))
145
- if bad_keys:
146
- bad_keys_fmt = ', '.join([f'"{i}"' for i in bad_keys])
147
- raise ConfigurationError(f'Unknown configuration options: {bad_keys_fmt}.')
148
-
149
- return config_dat, config_file
150
-
151
- @staticmethod
152
- def set_config(config_dir=None, raise_on_set=False):
153
- 'Load configuration from a YAML file.'
154
-
155
- config_dir = Config.resolve_config_dir(config_dir)
156
-
157
- if Config.is_set:
158
- if raise_on_set:
159
- raise ConfigurationError('Configuration is already set.')
160
- return
161
-
162
- config_dat, _ = Config.get_config_file(config_dir)
163
-
164
- profiles_dir = config_dir.joinpath('profiles')
165
- if not profiles_dir.is_dir():
166
- print('Profiles directory does not exist. Generating.')
167
- profiles_dir.mkdir()
168
-
169
- projects_DB_dir = config_dir.joinpath('projects')
170
- if not projects_DB_dir.is_dir():
171
- print('Projects database directory does not exist. Generating.')
172
- projects_DB_dir.mkdir()
173
-
174
- yaml = YAML()
175
- var_look_file = config_dir.joinpath('variable_lookup.yml')
176
- if not var_look_file.is_file():
177
- print('No variable lookup file found. Generating.')
178
- var_look_dat = {'variable_templates': {}, 'scopes': {}}
179
- yaml.dump(var_look_dat, var_look_file)
180
- else:
181
- var_look_dat = yaml.load(var_look_file)
182
-
183
- if sorted(var_look_dat.keys()) != ['scopes', 'variable_templates']:
184
- msg = (f'Variable lookup file must have keys "scopes" (dict) and '
185
- f'"variable_templates" (dict): {var_look_file}.')
186
- raise ConfigurationError(msg)
187
-
188
- Config.__conf.update(Config.__ALLOWED)
189
- Config.__conf.update(config_dat)
190
- Config.__conf.update({
191
- 'config_dir': config_dir,
192
- 'profiles_dir': profiles_dir,
193
- 'projects_DB_dir': projects_DB_dir,
194
- 'variable_lookup': var_look_dat,
195
- })
196
- Config.__conf.update(Config.__CONSTANTS)
197
- Config.__conf['cmd_group_defaults']['output_dir'] = (
198
- Config.__conf['default_output_dir']
199
- )
200
- Config.__conf['cmd_group_defaults']['error_dir'] = (
201
- Config.__conf['default_error_dir']
202
- )
203
-
204
- Config.is_set = True
205
-
206
- @staticmethod
207
- def get(name):
208
- if not Config.is_set:
209
- raise ConfigurationError('Configuration is not yet set.')
210
- return Config.__conf[name]
211
-
212
- @staticmethod
213
- def write_config_file(config_dat, config_file):
214
- yaml = YAML(typ='rt')
215
- yaml.dump(config_dat, config_file)
216
-
217
- @staticmethod
218
- def update(name, value, config_dir=None):
219
- 'Update the config file.'
220
- if name not in Config.__ALLOWED:
221
- msg = f'Cannot update configuration; value "{name}" is not allowed.'
222
- raise ConfigurationError(msg)
223
- config_dir = Config.resolve_config_dir(config_dir)
224
- config_dat, config_file = Config.get_config_file(
225
- config_dir,
226
- round_trip_load=True,
227
- quiet=True,
228
- )
229
- config_dat.update({name: value})
230
- Config.write_config_file(config_dat, config_file)
231
- if Config.is_set:
232
- Config.__conf[name] = value