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