hpcflow-new2 0.2.0a222__tar.gz → 0.2.0a227__tar.gz

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 (226) hide show
  1. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/PKG-INFO +1 -1
  2. hpcflow_new2-0.2.0a227/hpcflow/_version.py +1 -0
  3. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/app.py +7 -6
  4. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/cli.py +8 -1
  5. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/enums.py +54 -26
  6. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/parameters.py +49 -7
  7. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/base.py +8 -5
  8. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/zarr.py +14 -1
  9. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/enums.py +8 -34
  10. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/jobscript.py +3 -0
  11. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/submission.py +72 -17
  12. hpcflow_new2-0.2.0a227/hpcflow/sdk/utils/patches.py +31 -0
  13. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_multi_path_sequences.py +16 -9
  14. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_persistence.py +40 -1
  15. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/pyproject.toml +2 -2
  16. hpcflow_new2-0.2.0a222/hpcflow/_version.py +0 -1
  17. hpcflow_new2-0.2.0a222/hpcflow/sdk/utils/patches.py +0 -12
  18. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/LICENSE +0 -0
  19. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/README.md +0 -0
  20. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/__init__.py +0 -0
  21. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/__pyinstaller/__init__.py +0 -0
  22. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/__pyinstaller/hook-hpcflow.py +0 -0
  23. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/app.py +0 -0
  24. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/cli.py +0 -0
  25. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/demo_data_manifest/__init__.py +0 -0
  26. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/demo_data_manifest/demo_data_manifest.json +0 -0
  27. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/__init__.py +0 -0
  28. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/bad_script.py +0 -0
  29. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +0 -0
  30. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +0 -0
  31. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/demo_task_1_parse_p3.py +0 -0
  32. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/do_nothing.py +0 -0
  33. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +0 -0
  34. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +0 -0
  35. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +0 -0
  36. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +0 -0
  37. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +0 -0
  38. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +0 -0
  39. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +0 -0
  40. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/generate_t1_file_01.py +0 -0
  41. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/import_future_script.py +0 -0
  42. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/input_file_generator_basic.py +0 -0
  43. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/input_file_generator_basic_FAIL.py +0 -0
  44. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +0 -0
  45. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in.py +0 -0
  46. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +0 -0
  47. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +0 -0
  48. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +0 -0
  49. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +0 -0
  50. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +0 -0
  51. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +0 -0
  52. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +0 -0
  53. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +0 -0
  54. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +0 -0
  55. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +0 -0
  56. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +0 -0
  57. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +0 -0
  58. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +0 -0
  59. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +0 -0
  60. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +0 -0
  61. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +0 -0
  62. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +0 -0
  63. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_in_json_out.py +0 -0
  64. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +0 -0
  65. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_in_obj.py +0 -0
  66. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_out_FAIL.py +0 -0
  67. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_out_obj.py +0 -0
  68. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +0 -0
  69. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_shell_env_vars.py +0 -0
  70. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/main_script_test_std_out_std_err.py +0 -0
  71. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/output_file_parser_basic.py +0 -0
  72. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/output_file_parser_basic_FAIL.py +0 -0
  73. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +0 -0
  74. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/parse_t1_file_01.py +0 -0
  75. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/scripts/script_exit_test.py +0 -0
  76. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/template_components/__init__.py +0 -0
  77. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/template_components/command_files.yaml +0 -0
  78. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/template_components/environments.yaml +0 -0
  79. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/template_components/parameters.yaml +0 -0
  80. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/template_components/task_schemas.yaml +0 -0
  81. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/workflows/__init__.py +0 -0
  82. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/data/workflows/workflow_1.yaml +0 -0
  83. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/examples.ipynb +0 -0
  84. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/__init__.py +0 -0
  85. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/cli_common.py +0 -0
  86. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/__init__.py +0 -0
  87. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/callbacks.py +0 -0
  88. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/cli.py +0 -0
  89. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/config.py +0 -0
  90. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/config_file.py +0 -0
  91. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/errors.py +0 -0
  92. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/config/types.py +0 -0
  93. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/__init__.py +0 -0
  94. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/actions.py +0 -0
  95. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/app_aware.py +0 -0
  96. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/cache.py +0 -0
  97. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/command_files.py +0 -0
  98. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/commands.py +0 -0
  99. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/element.py +0 -0
  100. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/environment.py +0 -0
  101. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/errors.py +0 -0
  102. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/execute.py +0 -0
  103. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/json_like.py +0 -0
  104. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/loop.py +0 -0
  105. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/loop_cache.py +0 -0
  106. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/object_list.py +0 -0
  107. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/rule.py +0 -0
  108. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/run_dir_files.py +0 -0
  109. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/skip_reason.py +0 -0
  110. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/task.py +0 -0
  111. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/task_schema.py +0 -0
  112. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/test_utils.py +0 -0
  113. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/types.py +0 -0
  114. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/utils.py +0 -0
  115. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/validation.py +0 -0
  116. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/workflow.py +0 -0
  117. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/core/zarr_io.py +0 -0
  118. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/__init__.py +0 -0
  119. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/config_file_schema.yaml +0 -0
  120. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/config_schema.yaml +0 -0
  121. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/environments_spec_schema.yaml +0 -0
  122. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/files_spec_schema.yaml +0 -0
  123. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/parameters_spec_schema.yaml +0 -0
  124. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/task_schema_spec_schema.yaml +0 -0
  125. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/data/workflow_spec_schema.yaml +0 -0
  126. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/demo/__init__.py +0 -0
  127. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/demo/cli.py +0 -0
  128. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/helper/__init__.py +0 -0
  129. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/helper/cli.py +0 -0
  130. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/helper/helper.py +0 -0
  131. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/helper/watcher.py +0 -0
  132. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/log.py +0 -0
  133. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/__init__.py +0 -0
  134. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/defaults.py +0 -0
  135. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/discovery.py +0 -0
  136. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/json.py +0 -0
  137. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/pending.py +0 -0
  138. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/store_resource.py +0 -0
  139. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/types.py +0 -0
  140. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/persistence/utils.py +0 -0
  141. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/runtime.py +0 -0
  142. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/__init__.py +0 -0
  143. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/schedulers/__init__.py +0 -0
  144. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/schedulers/direct.py +0 -0
  145. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/schedulers/sge.py +0 -0
  146. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/schedulers/slurm.py +0 -0
  147. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/schedulers/utils.py +0 -0
  148. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/shells/__init__.py +0 -0
  149. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/shells/base.py +0 -0
  150. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/shells/bash.py +0 -0
  151. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/shells/os_version.py +0 -0
  152. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/shells/powershell.py +0 -0
  153. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/submission/types.py +0 -0
  154. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/typing.py +0 -0
  155. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/utils/arrays.py +0 -0
  156. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/utils/deferred_file.py +0 -0
  157. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/utils/hashing.py +0 -0
  158. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/sdk/utils/strings.py +0 -0
  159. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/api/test_api.py +0 -0
  160. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/conftest.py +0 -0
  161. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/__init__.py +0 -0
  162. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/benchmark_N_elements.yaml +0 -0
  163. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/benchmark_script_runner.yaml +0 -0
  164. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/multi_path_sequences.yaml +0 -0
  165. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/workflow_1.json +0 -0
  166. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/workflow_1.yaml +0 -0
  167. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/workflow_1_slurm.yaml +0 -0
  168. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/workflow_1_wsl.yaml +0 -0
  169. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/data/workflow_test_run_abort.yaml +0 -0
  170. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +0 -0
  171. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/schedulers/sge/test_sge_submission.py +0 -0
  172. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/schedulers/slurm/test_slurm_submission.py +0 -0
  173. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/scripts/test_input_file_generators.py +0 -0
  174. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/scripts/test_main_scripts.py +0 -0
  175. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/scripts/test_non_snippet_script.py +0 -0
  176. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/scripts/test_ouput_file_parsers.py +0 -0
  177. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/shells/wsl/test_wsl_submission.py +0 -0
  178. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_action.py +0 -0
  179. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_action_rule.py +0 -0
  180. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_app.py +0 -0
  181. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_cache.py +0 -0
  182. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_cli.py +0 -0
  183. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_command.py +0 -0
  184. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_config.py +0 -0
  185. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_config_file.py +0 -0
  186. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_element.py +0 -0
  187. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_element_iteration.py +0 -0
  188. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_element_set.py +0 -0
  189. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_group.py +0 -0
  190. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_input_source.py +0 -0
  191. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_input_value.py +0 -0
  192. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_jobscript_unit.py +0 -0
  193. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_json_like.py +0 -0
  194. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_loop.py +0 -0
  195. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_meta_task.py +0 -0
  196. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_object_list.py +0 -0
  197. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_parameter.py +0 -0
  198. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_resources.py +0 -0
  199. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_run.py +0 -0
  200. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_run_directories.py +0 -0
  201. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_runtime.py +0 -0
  202. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_schema_input.py +0 -0
  203. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_shell.py +0 -0
  204. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_slurm.py +0 -0
  205. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_submission.py +0 -0
  206. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_task.py +0 -0
  207. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_task_schema.py +0 -0
  208. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_utils.py +0 -0
  209. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_value_sequence.py +0 -0
  210. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_workflow.py +0 -0
  211. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/test_workflow_template.py +0 -0
  212. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_arrays.py +0 -0
  213. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_deferred_file_writer.py +0 -0
  214. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_hashing.py +0 -0
  215. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_patches.py +0 -0
  216. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_redirect_std.py +0 -0
  217. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/unit/utils/test_strings.py +0 -0
  218. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/__init__.py +0 -0
  219. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_directory_structure.py +0 -0
  220. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_jobscript.py +0 -0
  221. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_run_status.py +0 -0
  222. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_skip_downstream.py +0 -0
  223. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_submission.py +0 -0
  224. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_workflows.py +0 -0
  225. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/tests/workflows/test_zip.py +0 -0
  226. {hpcflow_new2-0.2.0a222 → hpcflow_new2-0.2.0a227}/hpcflow/viz_demo.ipynb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hpcflow-new2
3
- Version: 0.2.0a222
3
+ Version: 0.2.0a227
4
4
  Summary: Computational workflow management
5
5
  License: MPL-2.0
6
6
  Author: aplowman
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0a227"
@@ -3315,12 +3315,13 @@ class BaseApp(metaclass=Singleton):
3315
3315
  out_item["deleted"] = True
3316
3316
 
3317
3317
  else:
3318
- if status:
3319
- status.update(
3320
- f"Reading workflow {file_dat_i['path']!r} submission info..."
3321
- )
3322
3318
  with wk_i._store.cache_ctx():
3323
3319
  sub = wk_i.submissions[file_dat_i["sub_idx"]]
3320
+ if status:
3321
+ status.update(
3322
+ f"Loading workflow {file_dat_i['path']!r} run metadata..."
3323
+ )
3324
+ sub.use_EARs_cache = True # pre-cache EARs of this submission
3324
3325
 
3325
3326
  all_jobscripts = sub._submission_parts[submit_time_str]
3326
3327
  out_item["jobscripts"] = all_jobscripts
@@ -3583,7 +3584,7 @@ class BaseApp(metaclass=Singleton):
3583
3584
  )
3584
3585
  all_cells["status"] = "/".join(
3585
3586
  js_state.rich_repr
3586
- for js_state in sorted(act_js_states, key=lambda x: x.value)
3587
+ for js_state in sorted(act_js_states, key=lambda x: x.id)
3587
3588
  )
3588
3589
  else:
3589
3590
  if deleted:
@@ -3645,7 +3646,7 @@ class BaseApp(metaclass=Singleton):
3645
3646
  all_cells["actions_compact"] = " | ".join(
3646
3647
  f"[{k.colour}]{k.symbol}[/{k.colour}]:{v}" # type: ignore
3647
3648
  for k, v in dict(
3648
- sorted(EAR_stat_count.items(), key=lambda x: x[0].value)
3649
+ sorted(EAR_stat_count.items(), key=lambda x: x[0].id)
3649
3650
  ).items()
3650
3651
  )
3651
3652
  else:
@@ -4,6 +4,7 @@ Command line interface implementation.
4
4
 
5
5
  from __future__ import annotations
6
6
  import contextlib
7
+ import datetime
7
8
  import json
8
9
  import os
9
10
  import time
@@ -772,6 +773,12 @@ def _make_submission_CLI(app: BaseApp):
772
773
  def get_login_nodes(scheduler: SGEPosix):
773
774
  pprint(scheduler.get_login_nodes())
774
775
 
776
+ class _DateTimeJSONEncoder(json.JSONEncoder):
777
+ def default(self, obj):
778
+ if isinstance(obj, datetime.datetime):
779
+ return obj.isoformat()
780
+ return super().default(obj)
781
+
775
782
  @submission.command()
776
783
  @click.option(
777
784
  "as_json",
@@ -784,7 +791,7 @@ def _make_submission_CLI(app: BaseApp):
784
791
  """Print known-submissions information as a formatted Python object."""
785
792
  out = app.get_known_submissions(as_json=as_json)
786
793
  if as_json:
787
- click.echo(json.dumps(out))
794
+ click.echo(json.dumps(out, cls=_DateTimeJSONEncoder))
788
795
  else:
789
796
  pprint(out)
790
797
 
@@ -26,45 +26,85 @@ class ActionScopeType(Enum):
26
26
 
27
27
 
28
28
  @dataclass(frozen=True)
29
- class _EARStatus:
29
+ class _ReportableStateData:
30
30
  """
31
- Model of the state of an EARStatus.
31
+ Model of the state of something that is renderable using a symbol and colour.
32
+
33
+ Notes
34
+ -----
35
+ This class is used as the value in the the enumeration subclasses
36
+ :py:class:`EARStatus` and :py:class:`JobscriptElementState`.
37
+
32
38
  """
33
39
 
34
- _value: int
35
- #: Symbol to use when rendering a status.
40
+ #: ID that distinguishes the state.
41
+ id: int
42
+ #: Symbol to use when rendering a state.
36
43
  symbol: str
37
- #: Colour to use when rendering a status.
44
+ #: Colour to use when rendering a state.
38
45
  colour: str
46
+ #: Documentation of the meaning of the state.
39
47
  __doc__: str = ""
40
48
 
41
49
 
42
- class EARStatus(_EARStatus, Enum):
50
+ class _ReportableStateEnum(Enum):
51
+ """Enumeration superclass for reportable state subclasses with some shared methods."""
52
+
53
+ @property
54
+ def id(self) -> int:
55
+ """
56
+ The integer ID associated with this state.
57
+ """
58
+ return self.value.id
59
+
60
+ @property
61
+ def colour(self) -> str:
62
+ """
63
+ The colour associated with this state.
64
+ """
65
+ return self.value.colour
66
+
67
+ @property
68
+ def symbol(self) -> str:
69
+ """
70
+ The symbol associated with this state.
71
+ """
72
+ return self.value.symbol
73
+
74
+ @property
75
+ def rich_repr(self) -> str:
76
+ """
77
+ Rich representation of this enumeration element.
78
+ """
79
+ return f"[{self.colour}]{self.symbol}[/{self.colour}]"
80
+
81
+
82
+ class EARStatus(_ReportableStateEnum):
43
83
  """Enumeration of all possible EAR statuses, and their associated status colour."""
44
84
 
45
85
  #: Not yet associated with a submission.
46
- pending = (
86
+ pending = _ReportableStateData(
47
87
  0,
48
88
  ".",
49
89
  "grey46",
50
90
  "Not yet associated with a submission.",
51
91
  )
52
92
  #: Associated with a prepared submission that is not yet submitted.
53
- prepared = (
93
+ prepared = _ReportableStateData(
54
94
  1,
55
95
  ".",
56
96
  "grey46",
57
97
  "Associated with a prepared submission that is not yet submitted.",
58
98
  )
59
99
  #: Submitted for execution.
60
- submitted = (
100
+ submitted = _ReportableStateData(
61
101
  2,
62
102
  ".",
63
103
  "grey46",
64
104
  "Submitted for execution.",
65
105
  )
66
106
  #: Executing now.
67
- running = (
107
+ running = _ReportableStateData(
68
108
  3,
69
109
  "●",
70
110
  "dodger_blue1",
@@ -72,7 +112,7 @@ class EARStatus(_EARStatus, Enum):
72
112
  )
73
113
  #: Not attempted due to a failure of an upstream action on which this depends,
74
114
  #: or a loop termination condition being satisfied.
75
- skipped = (
115
+ skipped = _ReportableStateData(
76
116
  4,
77
117
  "s",
78
118
  "dark_orange",
@@ -82,32 +122,27 @@ class EARStatus(_EARStatus, Enum):
82
122
  ),
83
123
  )
84
124
  #: Aborted by the user; downstream actions will be attempted.
85
- aborted = (
125
+ aborted = _ReportableStateData(
86
126
  5,
87
127
  "A",
88
128
  "deep_pink4",
89
129
  "Aborted by the user; downstream actions will be attempted.",
90
130
  )
91
131
  #: Probably exited successfully.
92
- success = (
132
+ success = _ReportableStateData(
93
133
  6,
94
134
  "■",
95
135
  "green3",
96
136
  "Probably exited successfully.",
97
137
  )
98
138
  #: Probably failed.
99
- error = (
139
+ error = _ReportableStateData(
100
140
  7,
101
141
  "E",
102
142
  "red3",
103
143
  "Probably failed.",
104
144
  )
105
145
 
106
- @property
107
- def value(self) -> int:
108
- #: The value of the status.
109
- return self._value
110
-
111
146
  @classmethod
112
147
  def get_non_running_submitted_states(cls) -> frozenset[EARStatus]:
113
148
  """Return the set of all non-running states, excluding those before submission."""
@@ -120,13 +155,6 @@ class EARStatus(_EARStatus, Enum):
120
155
  }
121
156
  )
122
157
 
123
- @property
124
- def rich_repr(self) -> str:
125
- """
126
- The rich representation of the value.
127
- """
128
- return f"[{self.colour}]{self.symbol}[/{self.colour}]"
129
-
130
158
 
131
159
  class InputSourceType(Enum):
132
160
  """
@@ -1584,7 +1584,7 @@ class MultiPathSequence(_BaseSequence):
1584
1584
  paths: Sequence[str],
1585
1585
  num_samples: int,
1586
1586
  *,
1587
- bounds: dict[str, Sequence[float]] | None = None,
1587
+ bounds: dict[str, dict[str, str | Sequence[float]]] | None = None,
1588
1588
  scramble: bool = True,
1589
1589
  strength: int = 1,
1590
1590
  optimization: Literal["random-cd", "lloyd"] | None = None,
@@ -1602,10 +1602,20 @@ class MultiPathSequence(_BaseSequence):
1602
1602
 
1603
1603
  bounds = bounds or {}
1604
1604
 
1605
- parameter_ranges = np.array([bounds.get(path, [0, 1]) for path in paths]).T
1605
+ scaling = np.asarray(
1606
+ [bounds.get(path, {}).get("scaling", "linear") for path in paths]
1607
+ )
1608
+
1609
+ # extents including defaults for unspecified:
1610
+ all_extents = [bounds.get(path, {}).get("extent", [0, 1]) for path in paths]
1606
1611
 
1607
- lower_bound = parameter_ranges[0]
1608
- upper_bound = parameter_ranges[1]
1612
+ # extents accounting for scaling type:
1613
+ extent = np.asarray(
1614
+ [
1615
+ np.log10(all_extents[i]) if scaling[i] == "log" else all_extents[i]
1616
+ for i in range(len(scaling))
1617
+ ]
1618
+ ).T
1609
1619
 
1610
1620
  try:
1611
1621
  sampler = LatinHypercube(**kwargs)
@@ -1614,9 +1624,15 @@ class MultiPathSequence(_BaseSequence):
1614
1624
  kwargs["seed"] = kwargs.pop("rng")
1615
1625
  sampler = LatinHypercube(**kwargs)
1616
1626
 
1617
- samples = scale(sampler.random(n=num_samples), lower_bound, upper_bound).T
1627
+ samples = scale(
1628
+ sampler.random(n=num_samples), l_bounds=extent[0], u_bounds=extent[1]
1629
+ )
1630
+
1631
+ for i in range(len(scaling)):
1632
+ if scaling[i] == "log":
1633
+ samples[:, i] = 10 ** samples[:, i]
1618
1634
 
1619
- return samples
1635
+ return samples.T
1620
1636
 
1621
1637
  @classmethod
1622
1638
  def from_latin_hypercube(
@@ -1624,7 +1640,7 @@ class MultiPathSequence(_BaseSequence):
1624
1640
  paths: Sequence[str],
1625
1641
  num_samples: int,
1626
1642
  *,
1627
- bounds: dict[str, Sequence[float]] | None = None,
1643
+ bounds: dict[str, dict[str, str | Sequence[float]]] | None = None,
1628
1644
  scramble: bool = True,
1629
1645
  strength: int = 1,
1630
1646
  optimization: Literal["random-cd", "lloyd"] | None = None,
@@ -1634,6 +1650,32 @@ class MultiPathSequence(_BaseSequence):
1634
1650
  ) -> Self:
1635
1651
  """
1636
1652
  Generate values from SciPy's latin hypercube sampler: :class:`scipy.stats.qmc.LatinHypercube`.
1653
+
1654
+ Parameters
1655
+ ----------
1656
+ paths : Sequence[str]
1657
+ List of dot-delimited paths within the parameter's nested data structure for which
1658
+ 'value' should be set.
1659
+ num_samples : int
1660
+ Number of random hypercube samples to take.
1661
+ bounds : dict[str, dict[str, str | Sequence[float]]] | None, optional
1662
+ Bounds dictionary structure which takes a path as a key and returns another dictionary
1663
+ which takes `scaling` and `extent` as keys. `extent` defines the width of the parameter
1664
+ space, and `scaling` defines whether to take logarithmically spaced samples ("log") or not ("linear"). By default,
1665
+ linear scaling and an extent between 0 and 1 is used.
1666
+ scramble : bool, optional
1667
+ See `scipy.stats.qmc.LatinHypercube`, by default True
1668
+ strength : int, optional
1669
+ See 'scipy.stats.qmc.LatinHypercube', by default 1
1670
+ optimization : Literal["random, optional
1671
+ See 'scipy.stats.qmc.LatinHypercube', by default None
1672
+ rng : _type_, optional
1673
+ See 'scipy.stats.qmc.LatinHypercube', by default None
1674
+
1675
+ Returns
1676
+ -------
1677
+ NDArray
1678
+ Array of hypercube samples.
1637
1679
  """
1638
1680
  kwargs = {
1639
1681
  "paths": paths,
@@ -1218,12 +1218,15 @@ class PersistentStore(
1218
1218
  @contextlib.contextmanager
1219
1219
  def cache_ctx(self) -> Iterator[None]:
1220
1220
  """Context manager for using the persistent element/iteration/run cache."""
1221
- self._use_cache = True
1222
- try:
1221
+ if self._use_cache:
1223
1222
  yield
1224
- finally:
1225
- self._use_cache = False
1226
- self._reset_cache()
1223
+ else:
1224
+ self._use_cache = True
1225
+ try:
1226
+ yield
1227
+ finally:
1228
+ self._use_cache = False
1229
+ self._reset_cache()
1227
1230
 
1228
1231
  @contextlib.contextmanager
1229
1232
  def parameters_metadata_cache(self):
@@ -18,6 +18,7 @@ from numpy.ma.core import MaskedArray
18
18
  import zarr # type: ignore
19
19
  from zarr.errors import BoundsCheckError # type: ignore
20
20
  from zarr.storage import DirectoryStore, FSStore # type: ignore
21
+ from zarr.util import guess_chunks # type: ignore
21
22
  from fsspec.implementations.zip import ZipFileSystem # type: ignore
22
23
  from rich.console import Console
23
24
  from numcodecs import MsgPack, VLenArray, blosc, Blosc, Zstd # type: ignore
@@ -59,6 +60,7 @@ from hpcflow.sdk.submission.submission import (
59
60
  SUBMISSION_SUBMIT_TIME_KEYS,
60
61
  )
61
62
  from hpcflow.sdk.utils.arrays import get_2D_idx, split_arr
63
+ from hpcflow.sdk.utils.patches import override_module_attrs
62
64
  from hpcflow.sdk.utils.strings import shorten_list_str
63
65
 
64
66
  if TYPE_CHECKING:
@@ -89,6 +91,11 @@ if TYPE_CHECKING:
89
91
  ListAny: TypeAlias = "list[Any]"
90
92
  #: Zarr attribute mapping context.
91
93
  ZarrAttrs: TypeAlias = "dict[str, Any]"
94
+ #: Soft lower limit for the number of bytes in an array chunk
95
+ _ARRAY_CHUNK_MIN: int = 500 * 1024 * 1024 # 500 MiB
96
+ #: Hard upper limit for the number of bytes in an array chunk. Should be lower than the
97
+ #: maximum buffer size of the blosc encoder, if we're using it (2 GiB)
98
+ _ARRAY_CHUNK_MAX: int = 1024 * 1024 * 1024 # 1 GiB
92
99
  _JS: TypeAlias = "dict[str, list[dict[str, dict]]]"
93
100
 
94
101
 
@@ -124,7 +131,13 @@ def _encode_numpy_array(
124
131
  new_idx = (
125
132
  max((int(i.removeprefix("arr_")) for i in param_arr_group.keys()), default=-1) + 1
126
133
  )
127
- param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj, chunks=obj.shape)
134
+ with override_module_attrs(
135
+ "zarr.util", {"CHUNK_MIN": _ARRAY_CHUNK_MIN, "CHUNK_MAX": _ARRAY_CHUNK_MAX}
136
+ ):
137
+ # `guess_chunks` also ensures chunk shape is at least 1 in each dimension:
138
+ chunk_shape = guess_chunks(obj.shape, obj.dtype.itemsize)
139
+
140
+ param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj, chunks=chunk_shape)
128
141
  type_lookup["arrays"].append([path, new_idx])
129
142
 
130
143
  return len(type_lookup["arrays"]) - 1
@@ -6,82 +6,56 @@ from __future__ import annotations
6
6
  from dataclasses import dataclass
7
7
  from enum import Enum
8
8
 
9
+ from hpcflow.sdk.core.enums import _ReportableStateData, _ReportableStateEnum
9
10
 
10
- @dataclass(frozen=True)
11
- class _JES:
12
- """
13
- Model of the state of a JobscriptElementState
14
- """
15
-
16
- _value: int
17
- #: The symbol used to render the state.
18
- symbol: str
19
- #: The colour used to render the state.
20
- colour: str
21
- __doc__: str = ""
22
11
 
23
-
24
- class JobscriptElementState(_JES, Enum):
12
+ class JobscriptElementState(_ReportableStateEnum):
25
13
  """Enumeration to convey a particular jobscript element state as reported by the
26
14
  scheduler."""
27
15
 
28
16
  #: Waiting for resource allocation.
29
- pending = (
17
+ pending = _ReportableStateData(
30
18
  0,
31
19
  "○",
32
20
  "yellow",
33
21
  "Waiting for resource allocation.",
34
22
  )
35
23
  #: Waiting for one or more dependencies to finish.
36
- waiting = (
24
+ waiting = _ReportableStateData(
37
25
  1,
38
26
  "◊",
39
27
  "grey46",
40
28
  "Waiting for one or more dependencies to finish.",
41
29
  )
42
30
  #: Executing now.
43
- running = (
31
+ running = _ReportableStateData(
44
32
  2,
45
33
  "●",
46
34
  "dodger_blue1",
47
35
  "Executing now.",
48
36
  )
49
37
  #: Previously submitted but is no longer active.
50
- finished = (
38
+ finished = _ReportableStateData(
51
39
  3,
52
40
  "■",
53
41
  "grey46",
54
42
  "Previously submitted but is no longer active.",
55
43
  )
56
44
  #: Cancelled by the user.
57
- cancelled = (
45
+ cancelled = _ReportableStateData(
58
46
  4,
59
47
  "C",
60
48
  "red3",
61
49
  "Cancelled by the user.",
62
50
  )
63
51
  #: The scheduler reports an error state.
64
- errored = (
52
+ errored = _ReportableStateData(
65
53
  5,
66
54
  "E",
67
55
  "red3",
68
56
  "The scheduler reports an error state.",
69
57
  )
70
58
 
71
- @property
72
- def value(self) -> int:
73
- """
74
- The numerical value of this state.
75
- """
76
- return self._value
77
-
78
- @property
79
- def rich_repr(self) -> str:
80
- """
81
- Rich representation of this enumeration element.
82
- """
83
- return f"[{self.colour}]{self.symbol}[/{self.colour}]"
84
-
85
59
 
86
60
  class SubmissionStatus(Enum):
87
61
  """
@@ -901,6 +901,7 @@ class Jobscript(JSONLike):
901
901
  )
902
902
 
903
903
  @property
904
+ @TimeIt.decorator
904
905
  def all_EAR_IDs(self) -> NDArray:
905
906
  """Return all run IDs of this jobscripts (across all blocks), removing missing
906
907
  run IDs (i.e. -1 values)"""
@@ -912,6 +913,8 @@ class Jobscript(JSONLike):
912
913
  """
913
914
  Description of EAR information for this jobscript.
914
915
  """
916
+ if self.submission._use_EARs_cache:
917
+ return [self.submission._EARs_cache[ear_id] for ear_id in self.all_EAR_IDs]
915
918
  return self.workflow.get_EARs_from_IDs(self.all_EAR_IDs)
916
919
 
917
920
  @property
@@ -11,6 +11,7 @@ from textwrap import indent
11
11
  from typing import Any, Literal, overload, TYPE_CHECKING
12
12
  from typing_extensions import override
13
13
  import warnings
14
+ from contextlib import contextmanager
14
15
 
15
16
 
16
17
  from hpcflow.sdk.utils.strings import shorten_list_str
@@ -135,6 +136,10 @@ class Submission(JSONLike):
135
136
  None # assigned on first access
136
137
  )
137
138
 
139
+ # updated in _submission_EARs_cache context manager:
140
+ self._use_EARs_cache = False
141
+ self._EARs_cache: dict[int, ElementActionRun] = {}
142
+
138
143
  if workflow:
139
144
  #: The workflow this is part of.
140
145
  self.workflow = workflow
@@ -226,6 +231,8 @@ class Submission(JSONLike):
226
231
  del dct["_workflow"]
227
232
  del dct["_index"]
228
233
  del dct["_submission_parts_lst"]
234
+ del dct["_use_EARs_cache"]
235
+ del dct["_EARs_cache"]
229
236
  return {k.lstrip("_"): v for k, v in dct.items()}
230
237
 
231
238
  @property
@@ -265,6 +272,30 @@ class Submission(JSONLike):
265
272
  ]
266
273
  return self._submission_parts_lst
267
274
 
275
+ @property
276
+ @TimeIt.decorator
277
+ def use_EARs_cache(self) -> bool:
278
+ """Whether to pre-cache all EARs associated with the submission."""
279
+ return self._use_EARs_cache
280
+
281
+ @use_EARs_cache.setter
282
+ @TimeIt.decorator
283
+ def use_EARs_cache(self, value: bool):
284
+ """Toggle the EAR caching facility."""
285
+ if self._use_EARs_cache == value:
286
+ return
287
+ self._use_EARs_cache = value
288
+ if value:
289
+ all_EAR_IDs = list(self.all_EAR_IDs)
290
+ self._EARs_cache = {
291
+ ear_ID: ear
292
+ for ear_ID, ear in zip(
293
+ all_EAR_IDs, self.workflow.get_EARs_from_IDs(all_EAR_IDs)
294
+ )
295
+ }
296
+ else:
297
+ self._EARs_cache = {} # reset the cache
298
+
268
299
  @TimeIt.decorator
269
300
  def get_start_time(self, submit_time: str) -> datetime | None:
270
301
  """Get the start time of a given submission part."""
@@ -283,17 +314,36 @@ class Submission(JSONLike):
283
314
  @TimeIt.decorator
284
315
  def start_time(self) -> datetime | None:
285
316
  """Get the first non-None start time over all submission parts."""
286
- times = (
287
- self.get_start_time(submit_time) for submit_time in self._submission_parts
288
- )
289
- return min((t for t in times if t is not None), default=None)
317
+ with self.using_EARs_cache():
318
+ times = (
319
+ self.get_start_time(submit_time) for submit_time in self._submission_parts
320
+ )
321
+ return min((t for t in times if t is not None), default=None)
290
322
 
291
323
  @property
292
324
  @TimeIt.decorator
293
325
  def end_time(self) -> datetime | None:
294
326
  """Get the final non-None end time over all submission parts."""
295
- times = (self.get_end_time(submit_time) for submit_time in self._submission_parts)
296
- return max((t for t in times if t is not None), default=None)
327
+ with self.using_EARs_cache():
328
+ times = (
329
+ self.get_end_time(submit_time) for submit_time in self._submission_parts
330
+ )
331
+ return max((t for t in times if t is not None), default=None)
332
+
333
+ @contextmanager
334
+ def using_EARs_cache(self):
335
+ """
336
+ A context manager to load and cache all EARs associated with this submission (and
337
+ its jobscripts).
338
+ """
339
+ if self.use_EARs_cache:
340
+ yield
341
+ else:
342
+ self.use_EARs_cache = True
343
+ try:
344
+ yield
345
+ finally:
346
+ self.use_EARs_cache = False
297
347
 
298
348
  @property
299
349
  def jobscripts(self) -> list[Jobscript]:
@@ -592,15 +642,18 @@ class Submission(JSONLike):
592
642
  """
593
643
  The IDs of all EARs in this submission.
594
644
  """
595
- return (i for js in self.jobscripts for i in js.all_EAR_IDs)
645
+ return (int(i) for js in self.jobscripts for i in js.all_EAR_IDs)
596
646
 
597
647
  @property
598
648
  @TimeIt.decorator
599
- def all_EARs(self) -> Iterable[ElementActionRun]:
649
+ def all_EARs(self) -> list[ElementActionRun]:
600
650
  """
601
651
  All EARs in this submission.
602
652
  """
603
- return (ear for js in self.jobscripts for ear in js.all_EARs)
653
+ if self.use_EARs_cache:
654
+ return list(self._EARs_cache.values())
655
+ else:
656
+ return self.workflow.get_EARs_from_IDs(self.all_EAR_IDs)
604
657
 
605
658
  @property
606
659
  @TimeIt.decorator
@@ -610,9 +663,10 @@ class Submission(JSONLike):
610
663
  @property
611
664
  @TimeIt.decorator
612
665
  def all_EARs_by_jobscript(self) -> list[list[ElementActionRun]]:
613
- ids = [i.all_EAR_IDs for i in self.jobscripts]
614
- all_EARs = {i.id_: i for i in self.workflow.get_EARs_from_IDs(self.all_EAR_IDs)}
615
- return [[all_EARs[i] for i in js_ids] for js_ids in ids]
666
+ all_EARs = {i.id_: i for i in self.all_EARs}
667
+ return [
668
+ [all_EARs[i] for i in js_ids] for js_ids in self.all_EARs_IDs_by_jobscript
669
+ ]
616
670
 
617
671
  @property
618
672
  @TimeIt.decorator
@@ -650,11 +704,12 @@ class Submission(JSONLike):
650
704
  """Get jobscripts that are active on this machine, and their active states."""
651
705
  # this returns: {JS_IDX: {BLOCK_IDX: {JS_ELEMENT_IDX: STATE}}}
652
706
  # TODO: query the scheduler once for all jobscripts?
653
- return {
654
- js.index: act_states
655
- for js in self.jobscripts
656
- if (act_states := js.get_active_states(as_json=as_json))
657
- }
707
+ with self.using_EARs_cache():
708
+ return {
709
+ js.index: act_states
710
+ for js in self.jobscripts
711
+ if (act_states := js.get_active_states(as_json=as_json))
712
+ }
658
713
 
659
714
  @TimeIt.decorator
660
715
  def _write_scripts(
@@ -0,0 +1,31 @@
1
+ from contextlib import contextmanager
2
+ from pathlib import Path
3
+ import sys
4
+ from typing import Any
5
+
6
+
7
+ def resolve_path(path):
8
+ """On Windows Python 3.8, 3.9, and 3.10, `Pathlib.resolve` does
9
+ not return an absolute path for non-existant paths, when it should.
10
+
11
+ See: https://github.com/python/cpython/issues/82852
12
+
13
+ """
14
+ # TODO: this only seems to be used in a test; remove?
15
+ return Path.cwd() / Path(path).resolve() # cwd is ignored if already absolute
16
+
17
+
18
+ @contextmanager
19
+ def override_module_attrs(module_name: str, overrides: dict[str, Any]):
20
+ """Context manager to temporarily override module-level attributes. The module must be
21
+ imported (i.e. within `sys.modules`)."""
22
+
23
+ module = sys.modules[module_name]
24
+ original_values = {k: getattr(module, k) for k in overrides}
25
+ try:
26
+ for k, v in overrides.items():
27
+ setattr(module, k, v)
28
+ yield
29
+ finally:
30
+ for k, v in original_values.items():
31
+ setattr(module, k, v)