hpcflow 0.1.9__py3-none-any.whl → 0.2.0a271__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. hpcflow/__init__.py +2 -11
  2. hpcflow/__pyinstaller/__init__.py +5 -0
  3. hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
  4. hpcflow/_version.py +1 -1
  5. hpcflow/app.py +43 -0
  6. hpcflow/cli.py +2 -462
  7. hpcflow/data/demo_data_manifest/__init__.py +3 -0
  8. hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
  9. hpcflow/data/jinja_templates/test/test_template.txt +8 -0
  10. hpcflow/data/programs/hello_world/README.md +1 -0
  11. hpcflow/data/programs/hello_world/hello_world.c +87 -0
  12. hpcflow/data/programs/hello_world/linux/hello_world +0 -0
  13. hpcflow/data/programs/hello_world/macos/hello_world +0 -0
  14. hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
  15. hpcflow/data/scripts/__init__.py +1 -0
  16. hpcflow/data/scripts/bad_script.py +2 -0
  17. hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
  18. hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
  19. hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
  20. hpcflow/data/scripts/do_nothing.py +2 -0
  21. hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
  22. hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
  23. hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
  24. hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
  25. hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
  26. hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
  27. hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
  28. hpcflow/data/scripts/generate_t1_file_01.py +7 -0
  29. hpcflow/data/scripts/import_future_script.py +7 -0
  30. hpcflow/data/scripts/input_file_generator_basic.py +3 -0
  31. hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
  32. hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
  33. hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
  34. hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
  35. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
  36. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
  37. hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
  38. hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
  39. hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
  40. hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
  41. hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
  42. hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
  43. hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
  44. hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
  45. hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
  46. hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
  47. hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
  48. hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
  49. hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
  50. hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
  51. hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
  52. hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
  53. hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
  54. hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
  55. hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
  56. hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
  57. hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
  58. hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
  59. hpcflow/data/scripts/output_file_parser_basic.py +3 -0
  60. hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
  61. hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
  62. hpcflow/data/scripts/parse_t1_file_01.py +4 -0
  63. hpcflow/data/scripts/script_exit_test.py +5 -0
  64. hpcflow/data/template_components/__init__.py +1 -0
  65. hpcflow/data/template_components/command_files.yaml +26 -0
  66. hpcflow/data/template_components/environments.yaml +13 -0
  67. hpcflow/data/template_components/parameters.yaml +14 -0
  68. hpcflow/data/template_components/task_schemas.yaml +139 -0
  69. hpcflow/data/workflows/workflow_1.yaml +5 -0
  70. hpcflow/examples.ipynb +1037 -0
  71. hpcflow/sdk/__init__.py +149 -0
  72. hpcflow/sdk/app.py +4266 -0
  73. hpcflow/sdk/cli.py +1479 -0
  74. hpcflow/sdk/cli_common.py +385 -0
  75. hpcflow/sdk/config/__init__.py +5 -0
  76. hpcflow/sdk/config/callbacks.py +246 -0
  77. hpcflow/sdk/config/cli.py +388 -0
  78. hpcflow/sdk/config/config.py +1410 -0
  79. hpcflow/sdk/config/config_file.py +501 -0
  80. hpcflow/sdk/config/errors.py +272 -0
  81. hpcflow/sdk/config/types.py +150 -0
  82. hpcflow/sdk/core/__init__.py +38 -0
  83. hpcflow/sdk/core/actions.py +3857 -0
  84. hpcflow/sdk/core/app_aware.py +25 -0
  85. hpcflow/sdk/core/cache.py +224 -0
  86. hpcflow/sdk/core/command_files.py +814 -0
  87. hpcflow/sdk/core/commands.py +424 -0
  88. hpcflow/sdk/core/element.py +2071 -0
  89. hpcflow/sdk/core/enums.py +221 -0
  90. hpcflow/sdk/core/environment.py +256 -0
  91. hpcflow/sdk/core/errors.py +1043 -0
  92. hpcflow/sdk/core/execute.py +207 -0
  93. hpcflow/sdk/core/json_like.py +809 -0
  94. hpcflow/sdk/core/loop.py +1320 -0
  95. hpcflow/sdk/core/loop_cache.py +282 -0
  96. hpcflow/sdk/core/object_list.py +933 -0
  97. hpcflow/sdk/core/parameters.py +3371 -0
  98. hpcflow/sdk/core/rule.py +196 -0
  99. hpcflow/sdk/core/run_dir_files.py +57 -0
  100. hpcflow/sdk/core/skip_reason.py +7 -0
  101. hpcflow/sdk/core/task.py +3792 -0
  102. hpcflow/sdk/core/task_schema.py +993 -0
  103. hpcflow/sdk/core/test_utils.py +538 -0
  104. hpcflow/sdk/core/types.py +447 -0
  105. hpcflow/sdk/core/utils.py +1207 -0
  106. hpcflow/sdk/core/validation.py +87 -0
  107. hpcflow/sdk/core/values.py +477 -0
  108. hpcflow/sdk/core/workflow.py +4820 -0
  109. hpcflow/sdk/core/zarr_io.py +206 -0
  110. hpcflow/sdk/data/__init__.py +13 -0
  111. hpcflow/sdk/data/config_file_schema.yaml +34 -0
  112. hpcflow/sdk/data/config_schema.yaml +260 -0
  113. hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
  114. hpcflow/sdk/data/files_spec_schema.yaml +5 -0
  115. hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
  116. hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
  117. hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
  118. hpcflow/sdk/demo/__init__.py +3 -0
  119. hpcflow/sdk/demo/cli.py +242 -0
  120. hpcflow/sdk/helper/__init__.py +3 -0
  121. hpcflow/sdk/helper/cli.py +137 -0
  122. hpcflow/sdk/helper/helper.py +300 -0
  123. hpcflow/sdk/helper/watcher.py +192 -0
  124. hpcflow/sdk/log.py +288 -0
  125. hpcflow/sdk/persistence/__init__.py +18 -0
  126. hpcflow/sdk/persistence/base.py +2817 -0
  127. hpcflow/sdk/persistence/defaults.py +6 -0
  128. hpcflow/sdk/persistence/discovery.py +39 -0
  129. hpcflow/sdk/persistence/json.py +954 -0
  130. hpcflow/sdk/persistence/pending.py +948 -0
  131. hpcflow/sdk/persistence/store_resource.py +203 -0
  132. hpcflow/sdk/persistence/types.py +309 -0
  133. hpcflow/sdk/persistence/utils.py +73 -0
  134. hpcflow/sdk/persistence/zarr.py +2388 -0
  135. hpcflow/sdk/runtime.py +320 -0
  136. hpcflow/sdk/submission/__init__.py +3 -0
  137. hpcflow/sdk/submission/enums.py +70 -0
  138. hpcflow/sdk/submission/jobscript.py +2379 -0
  139. hpcflow/sdk/submission/schedulers/__init__.py +281 -0
  140. hpcflow/sdk/submission/schedulers/direct.py +233 -0
  141. hpcflow/sdk/submission/schedulers/sge.py +376 -0
  142. hpcflow/sdk/submission/schedulers/slurm.py +598 -0
  143. hpcflow/sdk/submission/schedulers/utils.py +25 -0
  144. hpcflow/sdk/submission/shells/__init__.py +52 -0
  145. hpcflow/sdk/submission/shells/base.py +229 -0
  146. hpcflow/sdk/submission/shells/bash.py +504 -0
  147. hpcflow/sdk/submission/shells/os_version.py +115 -0
  148. hpcflow/sdk/submission/shells/powershell.py +352 -0
  149. hpcflow/sdk/submission/submission.py +1402 -0
  150. hpcflow/sdk/submission/types.py +140 -0
  151. hpcflow/sdk/typing.py +194 -0
  152. hpcflow/sdk/utils/arrays.py +69 -0
  153. hpcflow/sdk/utils/deferred_file.py +55 -0
  154. hpcflow/sdk/utils/hashing.py +16 -0
  155. hpcflow/sdk/utils/patches.py +31 -0
  156. hpcflow/sdk/utils/strings.py +69 -0
  157. hpcflow/tests/api/test_api.py +32 -0
  158. hpcflow/tests/conftest.py +123 -0
  159. hpcflow/tests/data/__init__.py +0 -0
  160. hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
  161. hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
  162. hpcflow/tests/data/multi_path_sequences.yaml +29 -0
  163. hpcflow/tests/data/workflow_1.json +10 -0
  164. hpcflow/tests/data/workflow_1.yaml +5 -0
  165. hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
  166. hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
  167. hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
  168. hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
  169. hpcflow/tests/programs/test_programs.py +180 -0
  170. hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
  171. hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
  172. hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
  173. hpcflow/tests/scripts/test_input_file_generators.py +282 -0
  174. hpcflow/tests/scripts/test_main_scripts.py +1361 -0
  175. hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
  176. hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
  177. hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
  178. hpcflow/tests/unit/test_action.py +1066 -0
  179. hpcflow/tests/unit/test_action_rule.py +24 -0
  180. hpcflow/tests/unit/test_app.py +132 -0
  181. hpcflow/tests/unit/test_cache.py +46 -0
  182. hpcflow/tests/unit/test_cli.py +172 -0
  183. hpcflow/tests/unit/test_command.py +377 -0
  184. hpcflow/tests/unit/test_config.py +195 -0
  185. hpcflow/tests/unit/test_config_file.py +162 -0
  186. hpcflow/tests/unit/test_element.py +666 -0
  187. hpcflow/tests/unit/test_element_iteration.py +88 -0
  188. hpcflow/tests/unit/test_element_set.py +158 -0
  189. hpcflow/tests/unit/test_group.py +115 -0
  190. hpcflow/tests/unit/test_input_source.py +1479 -0
  191. hpcflow/tests/unit/test_input_value.py +398 -0
  192. hpcflow/tests/unit/test_jobscript_unit.py +757 -0
  193. hpcflow/tests/unit/test_json_like.py +1247 -0
  194. hpcflow/tests/unit/test_loop.py +2674 -0
  195. hpcflow/tests/unit/test_meta_task.py +325 -0
  196. hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
  197. hpcflow/tests/unit/test_object_list.py +116 -0
  198. hpcflow/tests/unit/test_parameter.py +243 -0
  199. hpcflow/tests/unit/test_persistence.py +664 -0
  200. hpcflow/tests/unit/test_resources.py +243 -0
  201. hpcflow/tests/unit/test_run.py +286 -0
  202. hpcflow/tests/unit/test_run_directories.py +29 -0
  203. hpcflow/tests/unit/test_runtime.py +9 -0
  204. hpcflow/tests/unit/test_schema_input.py +372 -0
  205. hpcflow/tests/unit/test_shell.py +129 -0
  206. hpcflow/tests/unit/test_slurm.py +39 -0
  207. hpcflow/tests/unit/test_submission.py +502 -0
  208. hpcflow/tests/unit/test_task.py +2560 -0
  209. hpcflow/tests/unit/test_task_schema.py +182 -0
  210. hpcflow/tests/unit/test_utils.py +616 -0
  211. hpcflow/tests/unit/test_value_sequence.py +549 -0
  212. hpcflow/tests/unit/test_values.py +91 -0
  213. hpcflow/tests/unit/test_workflow.py +827 -0
  214. hpcflow/tests/unit/test_workflow_template.py +186 -0
  215. hpcflow/tests/unit/utils/test_arrays.py +40 -0
  216. hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
  217. hpcflow/tests/unit/utils/test_hashing.py +65 -0
  218. hpcflow/tests/unit/utils/test_patches.py +5 -0
  219. hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
  220. hpcflow/tests/unit/utils/test_strings.py +97 -0
  221. hpcflow/tests/workflows/__init__.py +0 -0
  222. hpcflow/tests/workflows/test_directory_structure.py +31 -0
  223. hpcflow/tests/workflows/test_jobscript.py +355 -0
  224. hpcflow/tests/workflows/test_run_status.py +198 -0
  225. hpcflow/tests/workflows/test_skip_downstream.py +696 -0
  226. hpcflow/tests/workflows/test_submission.py +140 -0
  227. hpcflow/tests/workflows/test_workflows.py +564 -0
  228. hpcflow/tests/workflows/test_zip.py +18 -0
  229. hpcflow/viz_demo.ipynb +6794 -0
  230. hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
  231. hpcflow-0.2.0a271.dist-info/METADATA +65 -0
  232. hpcflow-0.2.0a271.dist-info/RECORD +237 -0
  233. {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
  234. hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
  235. hpcflow/api.py +0 -458
  236. hpcflow/archive/archive.py +0 -308
  237. hpcflow/archive/cloud/cloud.py +0 -47
  238. hpcflow/archive/cloud/errors.py +0 -9
  239. hpcflow/archive/cloud/providers/dropbox.py +0 -432
  240. hpcflow/archive/errors.py +0 -5
  241. hpcflow/base_db.py +0 -4
  242. hpcflow/config.py +0 -232
  243. hpcflow/copytree.py +0 -66
  244. hpcflow/data/examples/_config.yml +0 -14
  245. hpcflow/data/examples/damask/demo/1.run.yml +0 -4
  246. hpcflow/data/examples/damask/demo/2.process.yml +0 -29
  247. hpcflow/data/examples/damask/demo/geom.geom +0 -2052
  248. hpcflow/data/examples/damask/demo/load.load +0 -1
  249. hpcflow/data/examples/damask/demo/material.config +0 -185
  250. hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
  251. hpcflow/data/examples/damask/inputs/load.load +0 -1
  252. hpcflow/data/examples/damask/inputs/material.config +0 -185
  253. hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
  254. hpcflow/data/examples/damask/profiles/damask.yml +0 -4
  255. hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
  256. hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
  257. hpcflow/data/examples/damask/profiles/default.yml +0 -6
  258. hpcflow/data/examples/thinking.yml +0 -177
  259. hpcflow/errors.py +0 -2
  260. hpcflow/init_db.py +0 -37
  261. hpcflow/models.py +0 -2549
  262. hpcflow/nesting.py +0 -9
  263. hpcflow/profiles.py +0 -455
  264. hpcflow/project.py +0 -81
  265. hpcflow/scheduler.py +0 -323
  266. hpcflow/utils.py +0 -103
  267. hpcflow/validation.py +0 -167
  268. hpcflow/variables.py +0 -544
  269. hpcflow-0.1.9.dist-info/METADATA +0 -168
  270. hpcflow-0.1.9.dist-info/RECORD +0 -45
  271. hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
  272. hpcflow-0.1.9.dist-info/top_level.txt +0 -1
  273. /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
  274. /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
  275. /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
hpcflow/sdk/runtime.py ADDED
@@ -0,0 +1,320 @@
1
+ """
2
+ Information about the Python runtime.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ from importlib import import_module
7
+ from logging import Logger
8
+ import os
9
+ import platform
10
+ import re
11
+ import socket
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Any, ClassVar
15
+
16
+ from rich.table import Table
17
+ from rich.console import Console
18
+
19
+
20
+ class RunTimeInfo:
21
+ """Get useful run-time information, including the executable name used to
22
+ invoke the CLI, in the case a PyInstaller-built executable was used.
23
+
24
+ Parameters
25
+ ----------
26
+ name:
27
+ Application name.
28
+ package_name:
29
+ Application package name.
30
+ version:
31
+ Application version.
32
+ logger:
33
+ Where to write logging versions.
34
+ """
35
+
36
+ def __init__(
37
+ self, name: str, package_name: str, version: str, logger: Logger
38
+ ) -> None:
39
+ is_frozen: bool = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
40
+ bundle_dir = (
41
+ sys._MEIPASS
42
+ if is_frozen and hasattr(sys, "_MEIPASS")
43
+ else os.path.dirname(os.path.abspath(__file__))
44
+ )
45
+
46
+ #: Application name.
47
+ self.name = name.split(".")[0] # if name is given as __name__ # TODO: what?
48
+ #: Application package name.
49
+ self.package_name = package_name
50
+ #: Application version.
51
+ self.version = version
52
+ #: Whether this is a frozen application.
53
+ self.is_frozen = is_frozen
54
+ #: Working directory.
55
+ self.working_dir = os.getcwd()
56
+ #: Where to write log messages.
57
+ self.logger = logger
58
+ #: Host that this is running on.
59
+ self.hostname = socket.gethostname()
60
+
61
+ #: Whether this application is inside iPython.
62
+ self.in_ipython = False
63
+ #: Whether this application is being used interactively.
64
+ self.is_interactive = False
65
+ #: Whether this application is being used in test mode.
66
+ self.in_pytest = False # set in `conftest.py`
67
+ #: Whether this application is being run from the CLI.
68
+ self.from_CLI = False # set in CLI
69
+
70
+ if self.is_frozen:
71
+ #: The bundle directory, if frozen.
72
+ self.bundle_dir = Path(bundle_dir)
73
+ else:
74
+ #: The path to Python itself.
75
+ self.python_executable_path = Path(sys.executable)
76
+
77
+ try:
78
+ get_ipython # type: ignore
79
+ except NameError:
80
+ pass
81
+ else:
82
+ self.in_ipython = True
83
+
84
+ if hasattr(sys, "ps1"):
85
+ self.is_interactive = True
86
+
87
+ #: The Python version.
88
+ self.python_version = platform.python_version()
89
+ #: Whether the application is in a virtual environment.
90
+ self.is_venv = hasattr(sys, "real_prefix") or sys.base_prefix != sys.prefix
91
+ #: Whether the application is in a Conda virtual environment.
92
+ self.is_conda_venv = "CONDA_PREFIX" in os.environ
93
+
94
+ #: From `sys.prefix`. If running in a virtual environment, this will point to the
95
+ #: environment directory. If not running in a virtual environment, this will
96
+ #: point to the Python installation root.
97
+ self.sys_prefix = getattr(sys, "prefix", None)
98
+ #: From `sys.base_prefix`. This will be equal to `sys_prefix` (`sys.prefix`) if
99
+ #: not running within a virtual environment. However, if running within a virtual
100
+ #: environment, this will be the Python installation directory, and `sys_prefix`
101
+ #: will be equal to the virtual environment directory.
102
+ self.sys_base_prefix = getattr(sys, "base_prefix", None)
103
+ #: The old base prefix, from `sys.real_prefix`. Compatibility version of
104
+ #: :py:attr:`sys_base_prefix`.
105
+ self.sys_real_prefix = getattr(sys, "real_prefix", None)
106
+ #: The Conda prefix, if defined.
107
+ self.conda_prefix = os.environ.get("CONDA_PREFIX")
108
+
109
+ try:
110
+ #: The virtual environment path.
111
+ self.venv_path: str | list[str] | None = self.__set_venv_path()
112
+ except ValueError:
113
+ self.venv_path = None
114
+
115
+ self.logger.debug(
116
+ f"is_frozen: {self.is_frozen!r}"
117
+ f"{f' ({self.executable_name!r})' if self.is_frozen else ''}"
118
+ )
119
+ self.logger.debug(
120
+ f"is_venv: {self.is_venv!r}"
121
+ f"{f' ({self.sys_prefix!r})' if self.is_venv else ''}"
122
+ )
123
+ self.logger.debug(
124
+ f"is_conda_venv: {self.is_conda_venv!r}"
125
+ f"{f' ({self.conda_prefix!r})' if self.is_conda_venv else ''}"
126
+ )
127
+
128
+ _PLAT_LOOKUP = {"win32": "win", "darwin": "macos"}
129
+ #: CPU architecture, e.g. "AMD64" (on Windows), "x86-64" (Linux, or Intel Macs),
130
+ #: and "arm64" (Mac with Apple silicon).
131
+ self.CPU_arch = platform.machine()
132
+ # Broadly defined operating system, typically: "win", "macos", or "linux".
133
+ self.platform = _PLAT_LOOKUP.get(sys.platform, sys.platform)
134
+
135
+ # TODO: investigate
136
+ # if self.is_venv and self.is_conda_venv:
137
+ # msg = (
138
+ # "Running in a nested virtual environment (conda and non-conda). "
139
+ # "Environments may not be re-activate in the same order in associated, "
140
+ # "subsequent invocations of hpcflow."
141
+ # )
142
+ # warnings.warn(msg)
143
+
144
+ def to_dict(self) -> dict[str, Any]:
145
+ """
146
+ Serialize this class as a dictionary.
147
+ """
148
+ out = {
149
+ "name": self.name,
150
+ "package_name": self.package_name,
151
+ "version": self.version,
152
+ "is_frozen": self.is_frozen,
153
+ "working_dir": self.working_dir,
154
+ "logger": self.logger,
155
+ "hostname": self.hostname,
156
+ "python_version": self.python_version,
157
+ "invocation_command": self.invocation_command,
158
+ "in_ipython": self.in_ipython,
159
+ "in_pytest": self.in_pytest,
160
+ "from_CLI": self.from_CLI,
161
+ "CPU_arch": self.CPU_arch,
162
+ "platform": self.platform,
163
+ }
164
+ if self.is_frozen:
165
+ out.update(
166
+ {
167
+ "executable_name": self.executable_name,
168
+ "resolved_executable_name": self.resolved_executable_name,
169
+ "executable_path": self.executable_path,
170
+ "resolved_executable_path": self.resolved_executable_path,
171
+ }
172
+ )
173
+ else:
174
+ out.update(
175
+ {
176
+ "is_interactive": self.is_interactive,
177
+ "script_path": self.script_path,
178
+ "resolved_script_path": self.resolved_script_path,
179
+ "python_executable_path": self.python_executable_path,
180
+ "is_venv": self.is_venv,
181
+ "is_conda_venv": self.is_conda_venv,
182
+ "sys_prefix": self.sys_prefix,
183
+ "sys_base_prefix": self.sys_base_prefix,
184
+ "sys_real_prefix": self.sys_real_prefix,
185
+ "conda_prefix": self.conda_prefix,
186
+ "venv_path": self.venv_path,
187
+ }
188
+ )
189
+ return out
190
+
191
+ def __repr__(self) -> str:
192
+ out = f"{self.__class__.__name__}("
193
+ out += ", ".join(f"{k}={v!r}" for k, v in self.to_dict().items())
194
+ return out
195
+
196
+ def __set_venv_path(self) -> str | list[str]:
197
+ out: list[str] = []
198
+ if self.sys_prefix is not None:
199
+ out.append(self.sys_prefix)
200
+ elif self.conda_prefix is not None:
201
+ out.append(self.conda_prefix)
202
+ if not out:
203
+ raise ValueError("Not running in a virtual environment!")
204
+ if len(out) == 1:
205
+ return out[0]
206
+ else:
207
+ return out
208
+
209
+ def get_activate_env_command(self):
210
+ """
211
+ Get the command to activate the virtual environment.
212
+ """
213
+ pass
214
+
215
+ def get_deactivate_env_command(self):
216
+ """
217
+ Get the command to deactivate the virtual environment.
218
+ """
219
+ pass
220
+
221
+ def show(self) -> None:
222
+ """
223
+ Display the information known by this class as a human-readable table.
224
+ """
225
+ tab = Table(show_header=False, box=None)
226
+ tab.add_column()
227
+ tab.add_column()
228
+ for k, v in self.to_dict().items():
229
+ tab.add_row(k, str(v))
230
+
231
+ console = Console()
232
+ console.print(tab)
233
+
234
+ @property
235
+ def executable_path(self) -> Path | None:
236
+ """Get the path that the user invoked to launch the frozen app, if the app is
237
+ frozen.
238
+
239
+ If the user launches the app via a symbolic link, then this returns that link,
240
+ whereas `executable_path_resolved` returns the actual frozen app path.
241
+
242
+ """
243
+ return Path(sys.argv[0]) if self.is_frozen else None
244
+
245
+ @property
246
+ def resolved_executable_path(self) -> Path | None:
247
+ """Get the resolved path to the frozen app that the user launched, if the app is
248
+ frozen.
249
+
250
+ In a one-file app, this is the path to the bootloader. In the one-folder app, this
251
+ is the path to the executable.
252
+
253
+ References
254
+ ----------
255
+ [1] https://pyinstaller.org/en/stable/runtime-information.html#using-sys-executable-and-sys-argv-0
256
+
257
+ """
258
+ return Path(sys.executable) if self.is_frozen else None
259
+
260
+ @property
261
+ def executable_name(self) -> str | None:
262
+ """Get the name of the frozen app executable, if the app is frozen.
263
+
264
+ If the user launches the app via a symbolic link, then this returns the name of
265
+ that link, whereas `resolved_executable_name` returns the actual frozen app file
266
+ name.
267
+
268
+ """
269
+ return None if (p := self.executable_path) is None else p.name
270
+
271
+ @property
272
+ def resolved_executable_name(self) -> str | None:
273
+ """Get the resolved name of the frozen app executable, if the app is frozen."""
274
+ return None if (p := self.resolved_executable_path) is None else p.name
275
+
276
+ @property
277
+ def script_path(self) -> Path | None:
278
+ """Get the path to the Python script used to invoked this instance of the app, if
279
+ the app is not frozen."""
280
+ return None if self.is_frozen else Path(sys.argv[0])
281
+
282
+ @property
283
+ def resolved_script_path(self) -> Path | None:
284
+ """Get the resolved path to the Python script used to invoked this instance of the
285
+ app, if the app is not frozen."""
286
+ return None if (p := self.script_path) is None else p.resolve()
287
+
288
+ # For removing a trailing '.cmd' from a filename
289
+ __CMD_TRIM: ClassVar[re.Pattern[str]] = re.compile(r"\.cmd$")
290
+
291
+ @property
292
+ def invocation_command(self) -> tuple[str, ...]:
293
+ """Get the command that was used to invoke this instance of the app."""
294
+ if self.is_frozen:
295
+ # (this also works if we are running tests using the frozen app)
296
+ return (str(self.resolved_executable_path),)
297
+ elif self.from_CLI:
298
+ script = str(self.resolved_script_path)
299
+ if os.name == "nt":
300
+ # cannot reproduce locally, but on Windows GHA runners, if pytest is
301
+ # invoked via `hpcflow test`, `resolved_script_path` seems to be the
302
+ # batch script wrapper (ending in .cmd) rather than the Python entry point
303
+ # itself, so trim if off:
304
+ script = self.__CMD_TRIM.sub("", script) # Work with 3.8 too
305
+ # script = script.removesuffix(".cmd")
306
+ if not Path(script).is_file():
307
+ # conda generates an `.exe` file
308
+ script = f"{script}.exe"
309
+ if not Path(script).is_file():
310
+ raise RuntimeError("Cannot locate invocation script.")
311
+ return (str(self.python_executable_path), script)
312
+ else:
313
+ app_module = import_module(self.package_name)
314
+ CLI_path = Path(*app_module.__path__, "cli.py")
315
+ return (str(self.python_executable_path), str(CLI_path))
316
+
317
+ @property
318
+ def is_apple_silicon(self) -> bool:
319
+ """Return True if running on Apple silicon."""
320
+ return self.platform == "macos" and self.CPU_arch == "arm64"
@@ -0,0 +1,3 @@
1
+ """
2
+ Subsystem for submitting work to schedulers for enactment.
3
+ """
@@ -0,0 +1,70 @@
1
+ """
2
+ Submission enumeration types.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+
9
+ from hpcflow.sdk.core.enums import _ReportableStateData, _ReportableStateEnum
10
+
11
+
12
+ class JobscriptElementState(_ReportableStateEnum):
13
+ """Enumeration to convey a particular jobscript element state as reported by the
14
+ scheduler."""
15
+
16
+ #: Waiting for resource allocation.
17
+ pending = _ReportableStateData(
18
+ 0,
19
+ "○",
20
+ "yellow",
21
+ "Waiting for resource allocation.",
22
+ )
23
+ #: Waiting for one or more dependencies to finish.
24
+ waiting = _ReportableStateData(
25
+ 1,
26
+ "◊",
27
+ "grey46",
28
+ "Waiting for one or more dependencies to finish.",
29
+ )
30
+ #: Executing now.
31
+ running = _ReportableStateData(
32
+ 2,
33
+ "●",
34
+ "dodger_blue1",
35
+ "Executing now.",
36
+ )
37
+ #: Previously submitted but is no longer active.
38
+ finished = _ReportableStateData(
39
+ 3,
40
+ "■",
41
+ "grey46",
42
+ "Previously submitted but is no longer active.",
43
+ )
44
+ #: Cancelled by the user.
45
+ cancelled = _ReportableStateData(
46
+ 4,
47
+ "C",
48
+ "red3",
49
+ "Cancelled by the user.",
50
+ )
51
+ #: The scheduler reports an error state.
52
+ errored = _ReportableStateData(
53
+ 5,
54
+ "E",
55
+ "red3",
56
+ "The scheduler reports an error state.",
57
+ )
58
+
59
+
60
+ class SubmissionStatus(Enum):
61
+ """
62
+ The overall status of a submission.
63
+ """
64
+
65
+ #: Not yet submitted.
66
+ PENDING = 0
67
+ #: All jobscripts submitted successfully.
68
+ SUBMITTED = 1
69
+ #: Some jobscripts submitted successfully.
70
+ PARTIALLY_SUBMITTED = 2