hpcflow-new2 0.2.0a162__tar.gz → 0.2.0a163__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 (156) hide show
  1. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/PKG-INFO +1 -1
  2. hpcflow_new2-0.2.0a163/hpcflow/_version.py +1 -0
  3. hpcflow_new2-0.2.0a163/hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
  4. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/app.py +29 -42
  5. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/cli.py +1 -1
  6. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/actions.py +63 -14
  7. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/command_files.py +6 -4
  8. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/commands.py +7 -0
  9. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/element.py +39 -8
  10. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/errors.py +16 -0
  11. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/object_list.py +26 -14
  12. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/parameters.py +21 -3
  13. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/task.py +107 -2
  14. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/task_schema.py +16 -1
  15. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/test_utils.py +5 -2
  16. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/workflow.py +93 -5
  17. hpcflow_new2-0.2.0a163/hpcflow/sdk/data/workflow_spec_schema.yaml +20 -0
  18. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/demo/cli.py +1 -1
  19. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/submission.py +21 -10
  20. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/scripts/test_main_scripts.py +60 -0
  21. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_action.py +16 -0
  22. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_element.py +27 -25
  23. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_element_set.py +32 -0
  24. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_parameter.py +11 -9
  25. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_resources.py +7 -9
  26. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_schema_input.py +8 -8
  27. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_task.py +26 -27
  28. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_task_schema.py +39 -8
  29. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_value_sequence.py +5 -0
  30. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_workflow.py +4 -9
  31. hpcflow_new2-0.2.0a163/hpcflow/tests/unit/test_workflow_template.py +156 -0
  32. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/pyproject.toml +2 -2
  33. hpcflow_new2-0.2.0a162/hpcflow/_version.py +0 -1
  34. hpcflow_new2-0.2.0a162/hpcflow/sdk/data/workflow_spec_schema.yaml +0 -64
  35. hpcflow_new2-0.2.0a162/hpcflow/tests/unit/test_workflow_template.py +0 -35
  36. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/README.md +0 -0
  37. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/__init__.py +0 -0
  38. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/__pyinstaller/__init__.py +0 -0
  39. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/__pyinstaller/hook-hpcflow.py +0 -0
  40. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/app.py +0 -0
  41. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/cli.py +0 -0
  42. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/demo_data_manifest/__init__.py +0 -0
  43. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/demo_data_manifest/demo_data_manifest.json +0 -0
  44. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/__init__.py +0 -0
  45. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +0 -0
  46. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +0 -0
  47. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/demo_task_1_parse_p3.py +0 -0
  48. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/generate_t1_file_01.py +0 -0
  49. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +0 -0
  50. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +0 -0
  51. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +0 -0
  52. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +0 -0
  53. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +0 -0
  54. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +0 -0
  55. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +0 -0
  56. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +0 -0
  57. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_in_json_out.py +0 -0
  58. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +0 -0
  59. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_in_obj.py +0 -0
  60. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_out_obj.py +0 -0
  61. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +0 -0
  62. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/scripts/parse_t1_file_01.py +0 -0
  63. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/template_components/__init__.py +0 -0
  64. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/template_components/command_files.yaml +0 -0
  65. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/template_components/environments.yaml +0 -0
  66. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/template_components/parameters.yaml +0 -0
  67. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/template_components/task_schemas.yaml +0 -0
  68. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/workflows/__init__.py +0 -0
  69. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/data/workflows/workflow_1.yaml +0 -0
  70. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/examples.ipynb +0 -0
  71. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/__init__.py +0 -0
  72. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/cli_common.py +0 -0
  73. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/__init__.py +0 -0
  74. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/callbacks.py +0 -0
  75. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/cli.py +0 -0
  76. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/config.py +0 -0
  77. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/config_file.py +0 -0
  78. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/config/errors.py +0 -0
  79. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/__init__.py +0 -0
  80. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/environment.py +0 -0
  81. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/json_like.py +0 -0
  82. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/loop.py +0 -0
  83. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/parallel.py +0 -0
  84. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/rule.py +0 -0
  85. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/run_dir_files.py +0 -0
  86. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/utils.py +0 -0
  87. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/validation.py +0 -0
  88. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/core/zarr_io.py +0 -0
  89. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/__init__.py +0 -0
  90. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/config_file_schema.yaml +0 -0
  91. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/config_schema.yaml +0 -0
  92. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/environments_spec_schema.yaml +0 -0
  93. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/files_spec_schema.yaml +0 -0
  94. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/parameters_spec_schema.yaml +0 -0
  95. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/data/task_schema_spec_schema.yaml +0 -0
  96. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/demo/__init__.py +0 -0
  97. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/helper/__init__.py +0 -0
  98. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/helper/cli.py +0 -0
  99. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/helper/helper.py +0 -0
  100. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/helper/watcher.py +0 -0
  101. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/log.py +0 -0
  102. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/__init__.py +0 -0
  103. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/base.py +0 -0
  104. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/json.py +0 -0
  105. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/pending.py +0 -0
  106. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/store_resource.py +0 -0
  107. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/utils.py +0 -0
  108. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/persistence/zarr.py +0 -0
  109. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/runtime.py +0 -0
  110. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/__init__.py +0 -0
  111. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/jobscript.py +0 -0
  112. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/jobscript_info.py +0 -0
  113. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/schedulers/__init__.py +0 -0
  114. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/schedulers/direct.py +0 -0
  115. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/schedulers/sge.py +0 -0
  116. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/schedulers/slurm.py +0 -0
  117. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/schedulers/utils.py +0 -0
  118. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/shells/__init__.py +0 -0
  119. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/shells/base.py +0 -0
  120. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/shells/bash.py +0 -0
  121. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/shells/os_version.py +0 -0
  122. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/submission/shells/powershell.py +0 -0
  123. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/sdk/typing.py +0 -0
  124. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/conftest.py +0 -0
  125. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/__init__.py +0 -0
  126. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/benchmark_N_elements.yaml +0 -0
  127. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/workflow_1.json +0 -0
  128. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/workflow_1.yaml +0 -0
  129. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/workflow_1_slurm.yaml +0 -0
  130. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/workflow_1_wsl.yaml +0 -0
  131. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/data/workflow_test_run_abort.yaml +0 -0
  132. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +0 -0
  133. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/schedulers/slurm/test_slurm_submission.py +0 -0
  134. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/shells/wsl/test_wsl_submission.py +0 -0
  135. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_action_rule.py +0 -0
  136. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_app.py +0 -0
  137. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_cli.py +0 -0
  138. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_command.py +0 -0
  139. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_config.py +0 -0
  140. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_config_file.py +0 -0
  141. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_element_iteration.py +0 -0
  142. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_input_source.py +0 -0
  143. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_input_value.py +0 -0
  144. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_json_like.py +0 -0
  145. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_loop.py +0 -0
  146. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_object_list.py +0 -0
  147. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_persistence.py +0 -0
  148. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_run.py +0 -0
  149. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_runtime.py +0 -0
  150. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_shell.py +0 -0
  151. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_slurm.py +0 -0
  152. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_submission.py +0 -0
  153. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/unit/test_utils.py +0 -0
  154. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/workflows/test_jobscript.py +0 -0
  155. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/hpcflow/tests/workflows/test_workflows.py +0 -0
  156. {hpcflow_new2-0.2.0a162 → hpcflow_new2-0.2.0a163}/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.0a162
3
+ Version: 0.2.0a163
4
4
  Summary: Computational workflow management
5
5
  License: MIT
6
6
  Author: aplowman
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0a163"
@@ -0,0 +1,7 @@
1
+ def main_script_test_direct_in_direct_out_env_spec(p1, env_spec):
2
+
3
+ # process
4
+ p2 = p1 + 100
5
+
6
+ # return outputs
7
+ return {"p2": env_spec}
@@ -729,49 +729,28 @@ class BaseApp(metaclass=Singleton):
729
729
 
730
730
  @TimeIt.decorator
731
731
  def _load_scripts(self):
732
- from setuptools import find_packages
733
732
 
734
733
  # TODO: load custom directories / custom functions (via decorator)
734
+ scripts_package = f"{self.package_name}.{self.scripts_dir}"
735
735
 
736
- app_module = import_module(self.package_name)
737
- root_scripts_dir = self.scripts_dir
738
-
739
- packages = find_packages(
740
- where=str(Path(app_module.__path__[0], *root_scripts_dir.split(".")))
741
- )
742
- packages = [root_scripts_dir] + [root_scripts_dir + "." + i for i in packages]
743
- packages = [self.package_name + "." + i for i in packages]
744
- num_root_dirs = len(root_scripts_dir.split(".")) + 1
736
+ try:
737
+ ctx = resources.as_file(resources.files(scripts_package))
738
+ except AttributeError:
739
+ # < python 3.9; `resource.path` deprecated since 3.11
740
+ ctx = resources.path(scripts_package, "")
745
741
 
746
742
  scripts = {}
747
- for pkg in packages:
748
- try:
749
- contents = (
750
- resource.name
751
- for resource in resources.files(pkg).iterdir()
752
- if resource.is_file()
753
- )
754
- _is_rsrc = lambda pkg, name: resources.files(pkg).joinpath(name).is_file()
755
-
756
- except AttributeError:
757
- # < python 3.9; `resource.contents` deprecated since 3.11
758
- contents = resources.contents(pkg)
759
- _is_rsrc = lambda pkg, name: resources.is_resource(pkg, name)
760
-
761
- script_names = (
762
- name for name in contents if name != "__init__.py" and _is_rsrc(pkg, name)
763
- )
764
-
765
- for i in script_names:
766
- script_key = "/".join(pkg.split(".")[num_root_dirs:] + [i])
767
- try:
768
- script_ctx = resources.as_file(resources.files(pkg).joinpath(i))
769
- except AttributeError:
770
- # < python 3.9; `resource.path` deprecated since 3.11
771
- script_ctx = resources.path(pkg, i)
772
-
773
- with script_ctx as script:
774
- scripts[script_key] = script
743
+ with ctx as path:
744
+ for dirpath, _, filenames in os.walk(path):
745
+ dirpath = Path(dirpath)
746
+ if dirpath.name == "__pycache__":
747
+ continue
748
+ for filename in filenames:
749
+ if filename == "__init__.py":
750
+ continue
751
+ val = dirpath.joinpath(filename)
752
+ key = str(val.relative_to(path).as_posix())
753
+ scripts[key] = Path(val)
775
754
 
776
755
  return scripts
777
756
 
@@ -1380,15 +1359,19 @@ class BaseApp(metaclass=Singleton):
1380
1359
  variables=variables,
1381
1360
  status=status,
1382
1361
  )
1383
- return wk.submit(
1362
+ submitted_js = wk.submit(
1384
1363
  JS_parallelism=JS_parallelism,
1385
1364
  wait=wait,
1386
1365
  add_to_known=add_to_known,
1387
- return_idx=return_idx,
1366
+ return_idx=True,
1388
1367
  tasks=tasks,
1389
1368
  cancel=cancel,
1390
1369
  status=status,
1391
1370
  )
1371
+ if return_idx:
1372
+ return (wk, submitted_js)
1373
+ else:
1374
+ return wk
1392
1375
 
1393
1376
  def _make_demo_workflow(
1394
1377
  self,
@@ -1553,15 +1536,19 @@ class BaseApp(metaclass=Singleton):
1553
1536
  store_kwargs=store_kwargs,
1554
1537
  variables=variables,
1555
1538
  )
1556
- return wk.submit(
1539
+ submitted_js = wk.submit(
1557
1540
  JS_parallelism=JS_parallelism,
1558
1541
  wait=wait,
1559
1542
  add_to_known=add_to_known,
1560
- return_idx=return_idx,
1543
+ return_idx=True,
1561
1544
  tasks=tasks,
1562
1545
  cancel=cancel,
1563
1546
  status=status,
1564
1547
  )
1548
+ if return_idx:
1549
+ return (wk, submitted_js)
1550
+ else:
1551
+ return wk
1565
1552
 
1566
1553
  def _submit_workflow(
1567
1554
  self,
@@ -173,7 +173,7 @@ def _make_API_CLI(app):
173
173
  status=status,
174
174
  )
175
175
  if print_idx:
176
- click.echo(out)
176
+ click.echo(out[1])
177
177
 
178
178
  @click.command(context_settings={"ignore_unknown_options": True})
179
179
  @click.argument("py_test_args", nargs=-1, type=click.UNPROCESSED)
@@ -16,6 +16,7 @@ from watchdog.utils.dirsnapshot import DirectorySnapshotDiff
16
16
  from hpcflow.sdk import app
17
17
  from hpcflow.sdk.core import ABORT_EXIT_CODE
18
18
  from hpcflow.sdk.core.errors import (
19
+ ActionEnvironmentMissingNameError,
19
20
  MissingCompatibleActionEnvironment,
20
21
  OutputFileParserNoOutputError,
21
22
  UnknownScriptDataKey,
@@ -458,14 +459,18 @@ class ElementActionRun:
458
459
  self._output_files = self.app.ElementOutputFiles(element_action_run=self)
459
460
  return self._output_files
460
461
 
462
+ @property
463
+ def env_spec(self) -> Dict[str, Any]:
464
+ return self.resources.environments[self.action.get_environment_name()]
465
+
461
466
  @TimeIt.decorator
462
467
  def get_resources(self):
463
468
  """Resolve specific resources for this EAR, considering all applicable scopes and
464
469
  template-level resources."""
465
470
  return self.element_iteration.get_resources(self.action)
466
471
 
467
- def get_environment_label(self) -> str:
468
- return self.action.get_environment_label()
472
+ def get_environment_spec(self) -> str:
473
+ return self.action.get_environment_spec()
469
474
 
470
475
  def get_environment(self) -> app.Environment:
471
476
  return self.action.get_environment()
@@ -542,6 +547,9 @@ class ElementActionRun:
542
547
  else:
543
548
  out[key] = val_i
544
549
 
550
+ if self.action.script_pass_env_spec:
551
+ out["env_spec"] = self.env_spec
552
+
545
553
  return out
546
554
 
547
555
  def get_input_values_direct(self, label_dict: bool = True):
@@ -562,6 +570,10 @@ class ElementActionRun:
562
570
  typ = i.path[len("inputs.") :]
563
571
  if typ in input_types:
564
572
  inputs[typ] = i.value
573
+
574
+ if self.action.script_pass_env_spec:
575
+ inputs["env_spec"] = self.env_spec
576
+
565
577
  return inputs
566
578
 
567
579
  def get_OFP_output_files(self) -> Dict[str, Union[str, List[str]]]:
@@ -585,6 +597,10 @@ class ElementActionRun:
585
597
  inputs = {}
586
598
  for inp_typ in self.action.output_file_parsers[0].inputs or []:
587
599
  inputs[inp_typ] = self.get(f"inputs.{inp_typ}")
600
+
601
+ if self.action.script_pass_env_spec:
602
+ inputs["env_spec"] = self.env_spec
603
+
588
604
  return inputs
589
605
 
590
606
  def get_OFP_outputs(self) -> Dict[str, Union[str, List[str]]]:
@@ -626,7 +642,7 @@ class ElementActionRun:
626
642
 
627
643
  # write the script if it is specified as a app data script, otherwise we assume
628
644
  # the script already exists in the working directory:
629
- snip_path = self.action.get_snippet_script_path(self.action.script)
645
+ snip_path = self.action.get_snippet_script_path(self.action.script, self.env_spec)
630
646
  if snip_path:
631
647
  script_name = snip_path.name
632
648
  source_str = self.action.compose_source(snip_path)
@@ -677,22 +693,23 @@ class ElementActionRun:
677
693
  "stdout"/"stderr").
678
694
  """
679
695
  self.app.persistence_logger.debug("EAR.compose_commands")
696
+ env_spec = self.env_spec
697
+
680
698
  for ifg in self.action.input_file_generators:
681
699
  # TODO: there should only be one at this stage if expanded?
682
- ifg.write_source(self.action)
700
+ ifg.write_source(self.action, env_spec)
683
701
 
684
702
  for ofp in self.action.output_file_parsers:
685
703
  # TODO: there should only be one at this stage if expanded?
686
704
  if ofp.output is None:
687
705
  raise OutputFileParserNoOutputError()
688
- ofp.write_source(self.action)
706
+ ofp.write_source(self.action, env_spec)
689
707
 
690
708
  if self.action.script:
691
709
  self.write_source(js_idx=jobscript.index, js_act_idx=JS_action_idx)
692
710
 
693
711
  command_lns = []
694
- env_label = self.action.get_environment_label()
695
- env = jobscript.submission.environments.get(env_label)
712
+ env = jobscript.submission.environments.get(**env_spec)
696
713
  if env.setup:
697
714
  command_lns += list(env.setup)
698
715
 
@@ -953,13 +970,24 @@ class ActionEnvironment(JSONLike):
953
970
  ),
954
971
  )
955
972
 
956
- environment: str # app.Environment
973
+ environment: Union[str, Dict[str, Any]]
957
974
  scope: Optional[app.ActionScope] = None
958
975
 
959
976
  def __post_init__(self):
960
977
  if self.scope is None:
961
978
  self.scope = self.app.ActionScope.any()
962
979
 
980
+ orig_env = copy.deepcopy(self.environment)
981
+ if isinstance(self.environment, str):
982
+ self.environment = {"name": self.environment}
983
+
984
+ if "name" not in self.environment:
985
+ raise ActionEnvironmentMissingNameError(
986
+ f"The action-environment environment specification must include a string "
987
+ f"`name` key, or be specified as string that is that name. Provided "
988
+ f"environment key was {orig_env!r}."
989
+ )
990
+
963
991
 
964
992
  class ActionRule(JSONLike):
965
993
  """Class to represent a rule/condition that must be True if an action is to be
@@ -1089,6 +1117,7 @@ class Action(JSONLike):
1089
1117
  script_data_out: Optional[str] = None,
1090
1118
  script_data_files_use_opt: Optional[bool] = False,
1091
1119
  script_exe: Optional[str] = None,
1120
+ script_pass_env_spec: Optional[bool] = False,
1092
1121
  abortable: Optional[bool] = False,
1093
1122
  input_file_generators: Optional[List[app.InputFileGenerator]] = None,
1094
1123
  output_file_parsers: Optional[List[app.OutputFileParser]] = None,
@@ -1117,6 +1146,7 @@ class Action(JSONLike):
1117
1146
  script_data_files_use_opt if not self.script_is_python else True
1118
1147
  )
1119
1148
  self.script_exe = script_exe.lower() if script_exe else None
1149
+ self.script_pass_env_spec = script_pass_env_spec
1120
1150
  self.environments = environments or [
1121
1151
  self.app.ActionEnvironment(environment="null_env")
1122
1152
  ]
@@ -1413,7 +1443,10 @@ class Action(JSONLike):
1413
1443
  commands=self.commands,
1414
1444
  )
1415
1445
 
1416
- def get_environment_label(self) -> str:
1446
+ def get_environment_name(self) -> str:
1447
+ return self.get_environment_spec()["name"]
1448
+
1449
+ def get_environment_spec(self) -> Dict[str, Any]:
1417
1450
  if not self._from_expand:
1418
1451
  raise RuntimeError(
1419
1452
  f"Cannot choose a single environment from this action because it is not "
@@ -1422,7 +1455,7 @@ class Action(JSONLike):
1422
1455
  return self.environments[0].environment
1423
1456
 
1424
1457
  def get_environment(self) -> app.Environment:
1425
- return self.app.envs.get(self.get_environment_label())
1458
+ return self.app.envs.get(**self.get_environment_spec())
1426
1459
 
1427
1460
  @staticmethod
1428
1461
  def is_snippet_script(script: str) -> bool:
@@ -1442,7 +1475,9 @@ class Action(JSONLike):
1442
1475
  return script
1443
1476
 
1444
1477
  @classmethod
1445
- def get_snippet_script_str(cls, script) -> str:
1478
+ def get_snippet_script_str(
1479
+ cls, script, env_spec: Optional[Dict[str, Any]] = None
1480
+ ) -> str:
1446
1481
  if not cls.is_snippet_script(script):
1447
1482
  raise ValueError(
1448
1483
  f"Must be an app-data script name (e.g. "
@@ -1450,14 +1485,24 @@ class Action(JSONLike):
1450
1485
  )
1451
1486
  pattern = r"\<\<script:(.*:?)\>\>"
1452
1487
  match_obj = re.match(pattern, script)
1453
- return match_obj.group(1)
1488
+ out = match_obj.group(1)
1489
+
1490
+ if env_spec:
1491
+ out = re.sub(
1492
+ pattern=r"\<\<env:(.*?)\>\>",
1493
+ repl=lambda match_obj: env_spec[match_obj.group(1)],
1494
+ string=out,
1495
+ )
1496
+ return out
1454
1497
 
1455
1498
  @classmethod
1456
- def get_snippet_script_path(cls, script_path) -> Path:
1499
+ def get_snippet_script_path(
1500
+ cls, script_path, env_spec: Optional[Dict[str, Any]] = None
1501
+ ) -> Path:
1457
1502
  if not cls.is_snippet_script(script_path):
1458
1503
  return False
1459
1504
 
1460
- path = cls.get_snippet_script_str(script_path)
1505
+ path = cls.get_snippet_script_str(script_path, env_spec)
1461
1506
  if path in cls.app.scripts:
1462
1507
  path = cls.app.scripts.get(path)
1463
1508
 
@@ -1527,7 +1572,9 @@ class Action(JSONLike):
1527
1572
  input_file_generators=[ifg],
1528
1573
  environments=[self.get_input_file_generator_action_env(ifg)],
1529
1574
  rules=main_rules + ifg.get_action_rules(),
1575
+ script_pass_env_spec=ifg.script_pass_env_spec,
1530
1576
  abortable=ifg.abortable,
1577
+ # TODO: add script_data_in etc? and to OFP?
1531
1578
  )
1532
1579
  act_i._task_schema = self.task_schema
1533
1580
  if ifg.input_file not in inp_files:
@@ -1558,6 +1605,7 @@ class Action(JSONLike):
1558
1605
  output_file_parsers=[ofp],
1559
1606
  environments=[self.get_output_file_parser_action_env(ofp)],
1560
1607
  rules=list(self.rules) + ofp.get_action_rules(),
1608
+ script_pass_env_spec=ofp.script_pass_env_spec,
1561
1609
  abortable=ofp.abortable,
1562
1610
  )
1563
1611
  act_i._task_schema = self.task_schema
@@ -1616,6 +1664,7 @@ class Action(JSONLike):
1616
1664
  script_data_in=self.script_data_in,
1617
1665
  script_data_out=self.script_data_out,
1618
1666
  script_exe=self.script_exe,
1667
+ script_pass_env_spec=self.script_pass_env_spec,
1619
1668
  environments=[self.get_commands_action_env()],
1620
1669
  abortable=self.abortable,
1621
1670
  rules=main_rules,
@@ -131,6 +131,7 @@ class InputFileGenerator(JSONLike):
131
131
  inputs: List[app.Parameter]
132
132
  script: str = None
133
133
  environment: app.Environment = None
134
+ script_pass_env_spec: Optional[bool] = False
134
135
  abortable: Optional[bool] = False
135
136
  rules: Optional[List[app.ActionRule]] = None
136
137
 
@@ -188,11 +189,11 @@ class InputFileGenerator(JSONLike):
188
189
  out = out.format(script_str=script_str, main_block=main_block)
189
190
  return out
190
191
 
191
- def write_source(self, action):
192
+ def write_source(self, action, env_spec: Dict[str, Any]):
192
193
 
193
194
  # write the script if it is specified as a snippet script, otherwise we assume
194
195
  # the script already exists in the working directory:
195
- snip_path = action.get_snippet_script_path(self.script)
196
+ snip_path = action.get_snippet_script_path(self.script, env_spec)
196
197
  if snip_path:
197
198
  source_str = self.compose_source(snip_path)
198
199
  with Path(snip_path.name).open("wt", newline="\n") as fp:
@@ -255,6 +256,7 @@ class OutputFileParser(JSONLike):
255
256
  inputs: List[str] = None
256
257
  outputs: List[str] = None
257
258
  options: Dict = None
259
+ script_pass_env_spec: Optional[bool] = False
258
260
  abortable: Optional[bool] = False
259
261
  save_files: Union[List[str], bool] = True
260
262
  clean_up: Optional[List[str]] = None
@@ -342,14 +344,14 @@ class OutputFileParser(JSONLike):
342
344
  out = out.format(script_str=script_str, main_block=main_block)
343
345
  return out
344
346
 
345
- def write_source(self, action):
347
+ def write_source(self, action, env_spec: Dict[str, Any]):
346
348
  if self.output is None:
347
349
  # might be used just for saving files:
348
350
  return
349
351
 
350
352
  # write the script if it is specified as a snippet script, otherwise we assume
351
353
  # the script already exists in the working directory:
352
- snip_path = action.get_snippet_script_path(self.script)
354
+ snip_path = action.get_snippet_script_path(self.script, env_spec)
353
355
  if snip_path:
354
356
  source_str = self.compose_source(snip_path)
355
357
  with Path(snip_path.name).open("wt", newline="\n") as fp:
@@ -107,6 +107,7 @@ class Command(JSONLike):
107
107
 
108
108
  file_regex = r"(\<\<file:{}\>\>?)"
109
109
  exe_script_regex = r"\<\<(executable|script):(.*?)\>\>"
110
+ env_specs_regex = r"\<\<env:(.*?)\>\>"
110
111
 
111
112
  # substitute executables:
112
113
  cmd_str = re.sub(
@@ -118,6 +119,12 @@ class Command(JSONLike):
118
119
  # executable command might itself contain variables defined in `variables`, and/or
119
120
  # an `<<args>>` variable::
120
121
  for var_key, var_val in (self.variables or {}).items():
122
+ # substitute any `<<env:>>` specifiers
123
+ var_val = re.sub(
124
+ pattern=env_specs_regex,
125
+ repl=lambda match_obj: EAR.env_spec[match_obj.group(1)],
126
+ string=var_val,
127
+ )
121
128
  cmd_str = cmd_str.replace(f"<<{var_key}>>", var_val)
122
129
  if "<<args>>" in cmd_str:
123
130
  args_str = " ".join(self.arguments or [])
@@ -199,6 +199,7 @@ class ElementResources(JSONLike):
199
199
  scheduler_args: Optional[Dict] = None
200
200
  shell_args: Optional[Dict] = None
201
201
  os_name: Optional[str] = None
202
+ environments: Optional[Dict] = None
202
203
 
203
204
  # SGE scheduler specific:
204
205
  SGE_parallel_env: str = None
@@ -241,16 +242,24 @@ class ElementResources(JSONLike):
241
242
  return hash(tuple((keys, vals)))
242
243
 
243
244
  exclude = ("time_limit",)
244
- sub_dicts = ("scheduler_args", "shell_args")
245
245
  dct = {k: copy.deepcopy(v) for k, v in self.__dict__.items() if k not in exclude}
246
- if "options" in dct.get("scheduler_args", []):
247
- dct["scheduler_args"]["options"] = _hash_dict(
248
- dct["scheduler_args"]["options"]
249
- )
250
246
 
251
- for k in sub_dicts:
252
- if k in dct:
253
- dct[k] = _hash_dict(dct[k])
247
+ scheduler_args = dct["scheduler_args"]
248
+ shell_args = dct["shell_args"]
249
+ envs = dct["environments"]
250
+
251
+ if isinstance(scheduler_args, dict):
252
+ if "options" in scheduler_args:
253
+ dct["scheduler_args"]["options"] = _hash_dict(scheduler_args["options"])
254
+ dct["scheduler_args"] = _hash_dict(dct["scheduler_args"])
255
+
256
+ if isinstance(shell_args, dict):
257
+ dct["shell_args"] = _hash_dict(shell_args)
258
+
259
+ if isinstance(envs, dict):
260
+ for k, v in envs.items():
261
+ dct["environments"][k] = _hash_dict(v)
262
+ dct["environments"] = _hash_dict(dct["environments"])
254
263
 
255
264
  return _hash_dict(dct)
256
265
 
@@ -878,6 +887,28 @@ class ElementIteration:
878
887
  # an EAR?" which would then allow us to test a resources-based action rule.
879
888
 
880
889
  resource_specs = copy.deepcopy(self.get("resources"))
890
+
891
+ env_spec = action.get_environment_spec()
892
+ env_name = env_spec["name"]
893
+
894
+ # set default env specifiers, if none set:
895
+ if "any" not in resource_specs:
896
+ resource_specs["any"] = {}
897
+ if "environments" not in resource_specs["any"]:
898
+ resource_specs["any"]["environments"] = {env_name: copy.deepcopy(env_spec)}
899
+
900
+ for scope, dat in resource_specs.items():
901
+ if "environments" in dat:
902
+ # keep only relevant user-provided environment specifiers:
903
+ resource_specs[scope]["environments"] = {
904
+ k: v for k, v in dat["environments"].items() if k == env_name
905
+ }
906
+ # merge user-provided specifiers into action specifiers:
907
+ resource_specs[scope]["environments"][env_name] = {
908
+ **resource_specs[scope]["environments"].get(env_name, {}),
909
+ **copy.deepcopy(env_spec),
910
+ }
911
+
881
912
  resources = {}
882
913
  for scope in action.get_possible_scopes()[::-1]:
883
914
  # loop in reverse so higher-specificity scopes take precedence:
@@ -95,6 +95,10 @@ class MissingActionEnvironment(Exception):
95
95
  pass
96
96
 
97
97
 
98
+ class ActionEnvironmentMissingNameError(Exception):
99
+ pass
100
+
101
+
98
102
  class FromSpecMissingObjectError(Exception):
99
103
  pass
100
104
 
@@ -391,3 +395,15 @@ class UnknownScriptDataKey(ValueError):
391
395
 
392
396
  class MissingVariableSubstitutionError(KeyError):
393
397
  pass
398
+
399
+
400
+ class EnvironmentPresetUnknownEnvironmentError(ValueError):
401
+ pass
402
+
403
+
404
+ class UnknownEnvironmentPresetError(ValueError):
405
+ pass
406
+
407
+
408
+ class MultipleEnvironmentsError(ValueError):
409
+ pass
@@ -4,6 +4,10 @@ from types import SimpleNamespace
4
4
  from hpcflow.sdk.core.json_like import ChildObjectSpec, JSONLike
5
5
 
6
6
 
7
+ class ObjectListMultipleMatchError(ValueError):
8
+ pass
9
+
10
+
7
11
  class ObjectList(JSONLike):
8
12
  """A list-like class that provides item access via a `get` method according to
9
13
  attributes or dict-keys.
@@ -116,14 +120,22 @@ class ObjectList(JSONLike):
116
120
  if not result:
117
121
  available = []
118
122
  for obj in self._objects:
119
- available.append({k: getattr(obj, k) for k in kwargs})
123
+ attr_vals = {}
124
+ for k in kwargs:
125
+ try:
126
+ attr_vals[k] = self._get_obj_attr(obj, k)
127
+ except (AttributeError, KeyError):
128
+ continue
129
+ available.append(attr_vals)
120
130
  raise ValueError(
121
131
  f"No {self._descriptor} objects with attributes: {kwargs}. Available "
122
132
  f"objects have attributes: {tuple(available)!r}."
123
133
  )
124
134
 
125
135
  elif len(result) > 1:
126
- raise ValueError(f"Multiple objects with attributes: {kwargs}.")
136
+ raise ObjectListMultipleMatchError(
137
+ f"Multiple objects with attributes: {kwargs}."
138
+ )
127
139
 
128
140
  return result[0]
129
141
 
@@ -571,23 +583,23 @@ class ResourceList(ObjectList):
571
583
  def get_scopes(self):
572
584
  return tuple(i.scope for i in self._objects)
573
585
 
574
- def merge_template_resources(self, temp_res_lst):
575
- """Merge lower-precedence template-level resources into this resource list."""
576
- for scope_i in temp_res_lst.get_scopes():
586
+ def merge_other(self, other):
587
+ """Merge lower-precedence other resource list into this resource list."""
588
+ for scope_i in other.get_scopes():
577
589
  try:
578
- es_scoped = self.get(scope=scope_i)
590
+ self_scoped = self.get(scope=scope_i)
579
591
  except ValueError:
580
- in_es = False
592
+ in_self = False
581
593
  else:
582
- in_es = True
594
+ in_self = True
583
595
 
584
- temp_res_scoped = temp_res_lst.get(scope=scope_i)
585
- if in_es:
586
- for k, v in temp_res_scoped._get_members().items():
587
- if getattr(es_scoped, k) is None:
588
- setattr(es_scoped, f"_{k}", v)
596
+ other_scoped = other.get(scope=scope_i)
597
+ if in_self:
598
+ for k, v in other_scoped._get_members().items():
599
+ if getattr(self_scoped, k) is None:
600
+ setattr(self_scoped, f"_{k}", copy.deepcopy(v))
589
601
  else:
590
- self.add_object(copy.deepcopy(temp_res_scoped))
602
+ self.add_object(copy.deepcopy(other_scoped))
591
603
 
592
604
 
593
605
  def index(obj_lst, obj):