fractal-server 2.15.6__tar.gz → 2.15.8__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 (237) hide show
  1. {fractal_server-2.15.6 → fractal_server-2.15.8}/PKG-INFO +4 -3
  2. fractal_server-2.15.8/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/security.py +1 -2
  4. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/job.py +1 -1
  5. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +2 -3
  6. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_functions_history.py +3 -2
  7. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_functions_task_version_update.py +1 -1
  8. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +19 -10
  9. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/submit.py +22 -12
  10. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task.py +1 -1
  11. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +1 -1
  12. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/base_runner.py +5 -4
  13. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/_slurm_config.py +1 -6
  14. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +89 -65
  15. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_ssh/runner.py +59 -4
  16. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_ssh/tar_commands.py +1 -2
  17. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_sudo/runner.py +1 -1
  18. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/runner.py +1 -1
  19. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/runner_functions.py +5 -5
  20. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/task_collection.py +3 -1
  21. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/config.py +70 -25
  22. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/ssh/_fabric.py +74 -79
  23. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/_utils.py +1 -1
  24. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_pixi.py +6 -2
  25. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_python_interpreter.py +1 -1
  26. {fractal_server-2.15.6 → fractal_server-2.15.8}/pyproject.toml +13 -13
  27. fractal_server-2.15.6/fractal_server/__init__.py +0 -1
  28. {fractal_server-2.15.6 → fractal_server-2.15.8}/LICENSE +0 -0
  29. {fractal_server-2.15.6 → fractal_server-2.15.8}/README.md +0 -0
  30. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/__main__.py +0 -0
  31. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/alembic.ini +0 -0
  32. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/__init__.py +0 -0
  33. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/db/__init__.py +0 -0
  34. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/__init__.py +0 -0
  35. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/linkusergroup.py +0 -0
  36. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/linkuserproject.py +0 -0
  37. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/user_settings.py +0 -0
  38. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/__init__.py +0 -0
  39. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/accounting.py +0 -0
  40. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/dataset.py +0 -0
  41. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/history.py +0 -0
  42. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/job.py +0 -0
  43. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/project.py +0 -0
  44. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/task.py +0 -0
  45. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/task_group.py +0 -0
  46. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/workflow.py +0 -0
  47. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/models/v2/workflowtask.py +0 -0
  48. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/__init__.py +0 -0
  49. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/__init__.py +0 -0
  50. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  51. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
  52. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
  53. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/project.py +0 -0
  54. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/task.py +0 -0
  55. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  56. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/__init__.py +0 -0
  57. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  58. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  59. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  60. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +0 -0
  61. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  62. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/history.py +0 -0
  63. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/images.py +0 -0
  64. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/job.py +0 -0
  65. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/pre_submission_checks.py +0 -0
  66. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/project.py +0 -0
  67. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
  68. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  69. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  70. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_collection_pixi.py +0 -0
  71. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  72. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/task_version_update.py +0 -0
  73. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  74. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  75. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  76. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/__init__.py +0 -0
  77. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  78. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/current_user.py +0 -0
  79. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/group.py +0 -0
  80. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/login.py +0 -0
  81. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/oauth.py +0 -0
  82. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/register.py +0 -0
  83. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/router.py +0 -0
  84. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/auth/users.py +0 -0
  85. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/aux/__init__.py +0 -0
  86. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/aux/_job.py +0 -0
  87. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/aux/_runner.py +0 -0
  88. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  89. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/routes/pagination.py +0 -0
  90. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/__init__.py +0 -0
  91. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/components.py +0 -0
  92. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/exceptions.py +0 -0
  93. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/__init__.py +0 -0
  94. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/call_command_wrapper.py +0 -0
  95. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/local/__init__.py +0 -0
  96. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/local/get_local_config.py +0 -0
  97. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/local/runner.py +0 -0
  98. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/__init__.py +0 -0
  99. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/_batching.py +0 -0
  100. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/_job_states.py +0 -0
  101. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +0 -0
  102. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/remote.py +0 -0
  103. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +0 -0
  104. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_ssh/__init__.py +0 -0
  105. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_ssh/run_subprocess.py +0 -0
  106. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
  107. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +0 -0
  108. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/filenames.py +0 -0
  109. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  110. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/shutdown.py +0 -0
  111. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/task_files.py +0 -0
  112. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/__init__.py +0 -0
  113. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/_local.py +0 -0
  114. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/_slurm_ssh.py +0 -0
  115. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/_slurm_sudo.py +0 -0
  116. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/db_tools.py +0 -0
  117. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  118. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  119. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/submit_workflow.py +0 -0
  120. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/v2/task_interface.py +0 -0
  121. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/runner/versions.py +0 -0
  122. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/__init__.py +0 -0
  123. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/user.py +0 -0
  124. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/user_group.py +0 -0
  125. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/user_settings.py +0 -0
  126. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/__init__.py +0 -0
  127. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/accounting.py +0 -0
  128. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/dataset.py +0 -0
  129. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/dumps.py +0 -0
  130. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/history.py +0 -0
  131. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/job.py +0 -0
  132. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/manifest.py +0 -0
  133. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/project.py +0 -0
  134. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
  135. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/task.py +0 -0
  136. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/task_group.py +0 -0
  137. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/workflow.py +0 -0
  138. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  139. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/security/__init__.py +0 -0
  140. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/security/signup_email.py +0 -0
  141. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/app/user_settings.py +0 -0
  142. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/data_migrations/2_14_10.py +0 -0
  143. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/data_migrations/README.md +0 -0
  144. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/data_migrations/tools.py +0 -0
  145. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/exceptions.py +0 -0
  146. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/gunicorn_fractal.py +0 -0
  147. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/images/__init__.py +0 -0
  148. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/images/models.py +0 -0
  149. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/images/status_tools.py +0 -0
  150. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/images/tools.py +0 -0
  151. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/logger.py +0 -0
  152. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/main.py +0 -0
  153. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/env.py +0 -0
  154. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/naming_convention.py +0 -0
  155. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  156. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  157. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  158. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
  159. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
  160. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +0 -0
  161. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  162. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  163. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  164. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  165. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  166. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  167. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  168. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/791ce783d3d8_add_indices.py +0 -0
  169. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  170. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  171. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  172. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  173. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +0 -0
  174. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  175. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  176. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  177. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +0 -0
  178. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  179. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  180. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
  181. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
  182. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +0 -0
  183. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +0 -0
  184. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +0 -0
  185. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  186. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  187. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  188. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
  189. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  190. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +0 -0
  191. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  192. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +0 -0
  193. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  194. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
  195. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/py.typed +0 -0
  196. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/ssh/__init__.py +0 -0
  197. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/string_tools.py +0 -0
  198. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/syringe.py +0 -0
  199. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/__init__.py +0 -0
  200. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/utils.py +0 -0
  201. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/__init__.py +0 -0
  202. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/__init__.py +0 -0
  203. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/_utils.py +0 -0
  204. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/collect.py +0 -0
  205. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/collect_pixi.py +0 -0
  206. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/deactivate.py +0 -0
  207. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/deactivate_pixi.py +0 -0
  208. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/reactivate.py +0 -0
  209. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/local/reactivate_pixi.py +0 -0
  210. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  211. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/collect.py +0 -0
  212. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/collect_pixi.py +0 -0
  213. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
  214. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/deactivate_pixi.py +0 -0
  215. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
  216. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/ssh/reactivate_pixi.py +0 -0
  217. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  218. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  219. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  220. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  221. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  222. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  223. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/pixi_1_extract.sh +0 -0
  224. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/pixi_2_install.sh +0 -0
  225. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/templates/pixi_3_post_install.sh +0 -0
  226. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_background.py +0 -0
  227. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_database.py +0 -0
  228. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  229. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/tasks/v2/utils_templates.py +0 -0
  230. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/types/__init__.py +0 -0
  231. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/types/validators/__init__.py +0 -0
  232. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/types/validators/_common_validators.py +0 -0
  233. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/types/validators/_filter_validators.py +0 -0
  234. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/types/validators/_workflow_task_arguments_validators.py +0 -0
  235. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/urls.py +0 -0
  236. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/utils.py +0 -0
  237. {fractal_server-2.15.6 → fractal_server-2.15.8}/fractal_server/zip_tools.py +0 -0
@@ -1,19 +1,20 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.15.6
3
+ Version: 2.15.8
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License: BSD-3-Clause
6
6
  Author: Tommaso Comparin
7
7
  Author-email: tommaso.comparin@exact-lab.it
8
- Requires-Python: >=3.11,<3.13
8
+ Requires-Python: >=3.11,<3.14
9
9
  Classifier: License :: OSI Approved :: BSD License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
13
14
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
14
15
  Requires-Dist: cryptography (>=45.0.3,<45.1.0)
15
16
  Requires-Dist: fabric (>=3.2.2,<3.3.0)
16
- Requires-Dist: fastapi (>=0.115.0,<0.116.0)
17
+ Requires-Dist: fastapi (>=0.116.0,<0.117.0)
17
18
  Requires-Dist: fastapi-users[oauth] (>=14,<15)
18
19
  Requires-Dist: gunicorn (>=23.0,<24.0)
19
20
  Requires-Dist: packaging (>=25.0.0,<26.0.0)
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.15.8"
@@ -73,10 +73,9 @@ class UserOAuth(SQLModel, table=True):
73
73
  is_active:
74
74
  is_superuser:
75
75
  is_verified:
76
- slurm_user:
77
- slurm_accounts:
78
76
  username:
79
77
  oauth_accounts:
78
+ settings:
80
79
  """
81
80
 
82
81
  __tablename__ = "user_oauth"
@@ -109,7 +109,7 @@ async def view_job(
109
109
 
110
110
  @router.get("/{job_id}/", response_model=JobReadV2)
111
111
  async def view_single_job(
112
- job_id: int = None,
112
+ job_id: int,
113
113
  show_tmp_logs: bool = False,
114
114
  user: UserOAuth = Depends(current_active_superuser),
115
115
  db: AsyncSession = Depends(get_async_db),
@@ -25,7 +25,6 @@ from fractal_server.app.routes.aux.validate_user_settings import (
25
25
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
26
26
  from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
27
27
  from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
28
- from fractal_server.app.schemas.v2 import TaskGroupReadV2
29
28
  from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
30
29
  from fractal_server.config import get_settings
31
30
  from fractal_server.logger import set_logger
@@ -52,7 +51,7 @@ async def deactivate_task_group(
52
51
  response: Response,
53
52
  superuser: UserOAuth = Depends(current_active_superuser),
54
53
  db: AsyncSession = Depends(get_async_db),
55
- ) -> TaskGroupReadV2:
54
+ ) -> TaskGroupActivityV2Read:
56
55
  """
57
56
  Deactivate task-group venv
58
57
  """
@@ -157,7 +156,7 @@ async def reactivate_task_group(
157
156
  response: Response,
158
157
  superuser: UserOAuth = Depends(current_active_superuser),
159
158
  db: AsyncSession = Depends(get_async_db),
160
- ) -> TaskGroupReadV2:
159
+ ) -> TaskGroupActivityV2Read:
161
160
  """
162
161
  Deactivate task-group venv
163
162
  """
@@ -102,8 +102,9 @@ async def _verify_workflow_and_dataset_access(
102
102
  Verify user access to a dataset/workflow pair.
103
103
 
104
104
  Args:
105
+ project_id:
106
+ workflow_id:
105
107
  dataset_id:
106
- workflow_task_id:
107
108
  user_id:
108
109
  db:
109
110
  """
@@ -148,7 +149,7 @@ async def get_wftask_check_owner(
148
149
  Args:
149
150
  project_id:
150
151
  dataset_id:
151
- workflow_task_id:
152
+ workflowtask_id:
152
153
  user_id:
153
154
  db:
154
155
  """
@@ -6,7 +6,7 @@ def get_new_workflow_task_meta(
6
6
  old_workflow_task_meta: dict | None,
7
7
  old_task_meta: dict | None,
8
8
  new_task_meta: dict | None,
9
- ) -> dict[str, Any]:
9
+ ) -> dict[str, Any] | None:
10
10
  """
11
11
  Prepare new meta field based on old/new tasks and old workflow task.
12
12
  """
@@ -231,11 +231,14 @@ async def _get_collection_task_group_activity_status_message(
231
231
  )
232
232
  task_group_activity_list = res.scalars().all()
233
233
  if len(task_group_activity_list) > 1:
234
- msg = (
235
- "\nWarning: "
234
+ msg_short = (
236
235
  "Expected only one TaskGroupActivityV2 associated to TaskGroup "
237
236
  f"{task_group_id}, found {len(task_group_activity_list)} "
238
237
  f"(IDs: {[tga.id for tga in task_group_activity_list]})."
238
+ )
239
+ logger.error(f"UnreachableBranchError: {msg_short}")
240
+ msg = (
241
+ f"\nWarning: {msg_short}\n"
239
242
  "Warning: this should have not happened, please contact an admin."
240
243
  )
241
244
  elif len(task_group_activity_list) == 1:
@@ -268,13 +271,16 @@ async def _verify_non_duplication_user_constraint(
268
271
  if duplicate:
269
272
  user = await db.get(UserOAuth, user_id)
270
273
  if len(duplicate) > 1:
274
+ error_msg = (
275
+ f"User '{user.email}' already owns {len(duplicate)} task "
276
+ f"groups with name='{pkg_name}' and {version=} "
277
+ f"(IDs: {[group.id for group in duplicate]})."
278
+ )
279
+ logger.error(f"UnreachableBranchError: {error_msg}")
271
280
  raise HTTPException(
272
281
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
273
282
  detail=(
274
- "Invalid state:\n"
275
- f"User '{user.email}' already owns {len(duplicate)} task "
276
- f"groups with name='{pkg_name}' and {version=} "
277
- f"(IDs: {[group.id for group in duplicate]}).\n"
283
+ f"Invalid state: {error_msg}\n"
278
284
  "This should have not happened: please contact an admin."
279
285
  ),
280
286
  )
@@ -310,13 +316,16 @@ async def _verify_non_duplication_group_constraint(
310
316
  if duplicate:
311
317
  user_group = await db.get(UserGroup, user_group_id)
312
318
  if len(duplicate) > 1:
319
+ error_msg = (
320
+ f"UserGroup '{user_group.name}' already owns "
321
+ f"{len(duplicate)} task groups with name='{pkg_name}' and "
322
+ f"{version=} (IDs: {[group.id for group in duplicate]}).\n"
323
+ )
324
+ logger.error(error_msg)
313
325
  raise HTTPException(
314
326
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
315
327
  detail=(
316
- "Invalid state:\n"
317
- f"UserGroup '{user_group.name}' already owns "
318
- f"{len(duplicate)} task groups with name='{pkg_name}' and "
319
- f"{version=} (IDs: {[group.id for group in duplicate]}).\n"
328
+ f"Invalid state:\n{error_msg}"
320
329
  "This should have not happened: please contact an admin."
321
330
  ),
322
331
  )
@@ -156,6 +156,28 @@ async def apply_workflow(
156
156
  if len(user_settings.slurm_accounts) > 0:
157
157
  job_create.slurm_account = user_settings.slurm_accounts[0]
158
158
 
159
+ # User appropriate FractalSSH object
160
+ if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
161
+ ssh_config = dict(
162
+ user=user_settings.ssh_username,
163
+ host=user_settings.ssh_host,
164
+ key_path=user_settings.ssh_private_key_path,
165
+ )
166
+ fractal_ssh_list = request.app.state.fractal_ssh_list
167
+ try:
168
+ fractal_ssh = fractal_ssh_list.get(**ssh_config)
169
+ except Exception as e:
170
+ logger.error(
171
+ "Could not get a valid SSH connection in the submit endpoint. "
172
+ f"Original error: '{str(e)}'."
173
+ )
174
+ raise HTTPException(
175
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
176
+ detail="Error in setting up the SSH connection.",
177
+ )
178
+ else:
179
+ fractal_ssh = None
180
+
159
181
  # Add new Job object to DB
160
182
  job = JobV2(
161
183
  project_id=project_id,
@@ -219,18 +241,6 @@ async def apply_workflow(
219
241
  await db.merge(job)
220
242
  await db.commit()
221
243
 
222
- # User appropriate FractalSSH object
223
- if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
224
- ssh_config = dict(
225
- user=user_settings.ssh_username,
226
- host=user_settings.ssh_host,
227
- key_path=user_settings.ssh_private_key_path,
228
- )
229
- fractal_ssh_list = request.app.state.fractal_ssh_list
230
- fractal_ssh = fractal_ssh_list.get(**ssh_config)
231
- else:
232
- fractal_ssh = None
233
-
234
244
  # Expunge user settings from db, to use in background task
235
245
  db.expunge(user_settings)
236
246
 
@@ -69,7 +69,7 @@ async def get_list_task(
69
69
  stm = stm.where(TaskV2.authors.icontains(author))
70
70
 
71
71
  res = await db.execute(stm)
72
- task_list = res.scalars().all()
72
+ task_list = list(res.scalars().all())
73
73
  await db.close()
74
74
  if args_schema is False:
75
75
  for task in task_list:
@@ -49,7 +49,7 @@ async def deactivate_task_group(
49
49
  response: Response,
50
50
  user: UserOAuth = Depends(current_active_user),
51
51
  db: AsyncSession = Depends(get_async_db),
52
- ) -> TaskGroupReadV2:
52
+ ) -> TaskGroupActivityV2Read:
53
53
  """
54
54
  Deactivate task-group venv
55
55
  """
@@ -91,14 +91,15 @@ class BaseRunner:
91
91
  workflow_task_order:
92
92
  workflow_task_id:
93
93
  task_name:
94
- parameters:
95
- Dictionary of parameters. Must include `zarr_urls` key.
94
+ list_parameters:
95
+ List of dictionaries of parameters (each one must include
96
+ `zarr_urls` key).
96
97
  history_unit_ids:
97
98
  Database IDs of the corresponding `HistoryUnit` entries.
99
+ list_task_files: `TaskFiles` objects.
98
100
  task_type: Task type.
99
- task_files: `TaskFiles` object.
100
101
  config: Runner-specific parameters.
101
- user_id
102
+ user_id:
102
103
  """
103
104
  raise NotImplementedError()
104
105
 
@@ -66,7 +66,6 @@ class _SlurmConfigSet(BaseModel):
66
66
  time: str | None = None
67
67
  account: str | None = None
68
68
  extra_lines: list[str] | None = None
69
- pre_submission_commands: list[str] | None = None
70
69
  gpus: str | None = None
71
70
 
72
71
 
@@ -213,7 +212,7 @@ class SlurmConfig(BaseModel):
213
212
  `SlurmConfig` attributes (e.g. `mem_per_task_MB`), which are not meant to
214
213
  be part of the `FRACTAL_SLURM_CONFIG_FILE` JSON file (details on the
215
214
  expected file content are defined in
216
- [`SlurmConfigFile`](./#fractal_server.app.runner._slurm._slurm_config.SlurmConfigFile)).
215
+ [`SlurmConfigFile`](#fractal_server.app.runner._slurm._slurm_config.SlurmConfigFile)).
217
216
 
218
217
  Part of the attributes map directly to some of the SLURM attributes (see
219
218
  https://slurm.schedmd.com/sbatch.html), e.g. `partition`. Other attributes
@@ -253,8 +252,6 @@ class SlurmConfig(BaseModel):
253
252
  Key-value pairs to be included as `export`-ed variables in SLURM
254
253
  submission script, after prepending values with the user's cache
255
254
  directory.
256
- pre_submission_commands: List of commands to be prepended to the sbatch
257
- command.
258
255
  """
259
256
 
260
257
  model_config = ConfigDict(extra="forbid")
@@ -294,8 +291,6 @@ class SlurmConfig(BaseModel):
294
291
  target_num_jobs: int
295
292
  max_num_jobs: int
296
293
 
297
- pre_submission_commands: list[str] = Field(default_factory=list)
298
-
299
294
  def _sorted_extra_lines(self) -> list[str]:
300
295
  """
301
296
  Return a copy of `self.extra_lines`, where lines starting with
@@ -137,7 +137,9 @@ class BaseSlurmRunner(BaseRunner):
137
137
  def run_squeue(self, *, job_ids: list[str], **kwargs) -> str:
138
138
  raise NotImplementedError("Implement in child class.")
139
139
 
140
- def _is_squeue_error_recoverable(self, exception: BaseException) -> True:
140
+ def _is_squeue_error_recoverable(
141
+ self, exception: BaseException
142
+ ) -> Literal[True]:
141
143
  """
142
144
  Determine whether a `squeue` error is considered recoverable.
143
145
 
@@ -262,14 +264,25 @@ class BaseSlurmRunner(BaseRunner):
262
264
 
263
265
  return new_slurm_config
264
266
 
265
- def _submit_single_sbatch(
267
+ def _prepare_single_slurm_job(
266
268
  self,
267
269
  *,
268
270
  base_command: str,
269
271
  slurm_job: SlurmJob,
270
272
  slurm_config: SlurmConfig,
271
273
  ) -> str:
272
- logger.debug("[_submit_single_sbatch] START")
274
+ """
275
+ Prepare submission script locally.
276
+
277
+ Args:
278
+ base_command: Base of task executable command.
279
+ slurm_job: `SlurmJob` object
280
+ slurm_config: Configuration for SLURM job
281
+
282
+ Returns:
283
+ Command to submit the SLURM job.
284
+ """
285
+ logger.debug("[_prepare_single_slurm_job] START")
273
286
 
274
287
  for task in slurm_job.tasks:
275
288
  # Write input file
@@ -299,24 +312,10 @@ class BaseSlurmRunner(BaseRunner):
299
312
  json.dump(task.parameters, f, indent=2)
300
313
 
301
314
  logger.debug(
302
- "[_submit_single_sbatch] Written " f"{task.input_file_local=}"
315
+ "[_prepare_single_slurm_job] Written "
316
+ f"{task.input_file_local=}"
303
317
  )
304
318
 
305
- if self.slurm_runner_type == "ssh":
306
- # Send input file (only relevant for SSH)
307
- self.fractal_ssh.send_file(
308
- local=task.input_file_local,
309
- remote=task.input_file_remote,
310
- )
311
- self.fractal_ssh.send_file(
312
- local=task.task_files.args_file_local,
313
- remote=task.task_files.args_file_remote,
314
- )
315
- logger.debug(
316
- "[_submit_single_sbatch] Transferred "
317
- f"{task.input_file_local=}"
318
- )
319
-
320
319
  # Prepare commands to be included in SLURM submission script
321
320
  cmdlines = []
322
321
  for task in slurm_job.tasks:
@@ -353,7 +352,7 @@ class BaseSlurmRunner(BaseRunner):
353
352
  ]
354
353
  )
355
354
  script_lines = slurm_config.sort_script_lines(script_lines)
356
- logger.debug(script_lines)
355
+ logger.debug(f"[_prepare_single_slurm_job] {script_lines=}")
357
356
 
358
357
  # Always print output of `uname -n` and `pwd`
359
358
  script_lines.append('\necho "Hostname: $(uname -n)"')
@@ -373,61 +372,64 @@ class BaseSlurmRunner(BaseRunner):
373
372
  f"--mem={mem_per_task_MB}MB "
374
373
  f"{cmd} &"
375
374
  )
376
- script_lines.append("wait\n")
377
- script = "\n".join(script_lines)
375
+ script_lines.append("wait\n\n")
378
376
  script_lines.append(
379
377
  'echo "End time: $(date +"%Y-%m-%dT%H:%M:%S%z")"'
380
378
  )
379
+ script = "\n".join(script_lines)
381
380
 
382
381
  # Write submission script
383
382
  with open(slurm_job.slurm_submission_script_local, "w") as f:
384
383
  f.write(script)
385
384
  logger.debug(
386
- "[_submit_single_sbatch] Written "
385
+ "[_prepare_single_slurm_job] Written "
387
386
  f"{slurm_job.slurm_submission_script_local=}"
388
387
  )
389
388
 
390
389
  if self.slurm_runner_type == "ssh":
391
- self.fractal_ssh.send_file(
392
- local=slurm_job.slurm_submission_script_local,
393
- remote=slurm_job.slurm_submission_script_remote,
394
- )
395
390
  submit_command = (
396
- "sbatch --parsable "
397
- f"{slurm_job.slurm_submission_script_remote}"
391
+ f"sbatch --parsable {slurm_job.slurm_submission_script_remote}"
398
392
  )
399
393
  else:
400
394
  submit_command = (
401
- "sbatch --parsable "
402
- f"{slurm_job.slurm_submission_script_local}"
395
+ f"sbatch --parsable {slurm_job.slurm_submission_script_local}"
403
396
  )
404
- # Run sbatch
405
- pre_submission_cmds = slurm_config.pre_submission_commands
406
- if len(pre_submission_cmds) == 0:
407
- logger.debug(f"Now run {submit_command=}")
408
- sbatch_stdout = self._run_remote_cmd(submit_command)
409
- else:
410
- logger.debug(f"Now using {pre_submission_cmds=}")
411
- script_lines = pre_submission_cmds + [submit_command]
412
- wrapper_script_contents = "\n".join(script_lines)
413
- wrapper_script_contents = f"{wrapper_script_contents}\n"
414
- if self.slurm_runner_type == "ssh":
415
- wrapper_script = (
416
- f"{slurm_job.slurm_submission_script_remote}_wrapper.sh"
417
- )
418
- self.fractal_ssh.write_remote_file(
419
- path=wrapper_script, content=wrapper_script_contents
420
- )
421
- else:
422
- wrapper_script = (
423
- f"{slurm_job.slurm_submission_script_local}_wrapper.sh"
424
- )
425
- with open(wrapper_script, "w") as f:
426
- f.write(wrapper_script_contents)
427
- logger.debug(f"Now run {wrapper_script=}")
428
- sbatch_stdout = self._run_remote_cmd(f"bash {wrapper_script}")
397
+ logger.debug("[_prepare_single_slurm_job] END")
398
+ return submit_command
399
+
400
+ def _send_many_job_inputs(
401
+ self, *, workdir_local: Path, workdir_remote: Path
402
+ ) -> None:
403
+ """
404
+ Placeholder method.
405
+
406
+ This method is intentionally left unimplemented in the base class.
407
+ Subclasses must override it to provide the logic for transferring
408
+ input data.
409
+ """
410
+ pass
411
+
412
+ def _submit_single_sbatch(
413
+ self,
414
+ *,
415
+ submit_command: str,
416
+ slurm_job: SlurmJob,
417
+ ) -> None:
418
+ """
419
+ Run `sbatch` and add the `slurm_job` to `self.jobs`.
420
+
421
+ Args:
422
+ submit_command:
423
+ The SLURM submission command prepared in
424
+ `self._prepare_single_slurm_job`.
425
+ slurm_job: The `SlurmJob` object.
426
+ """
427
+
428
+ logger.debug("[_submit_single_sbatch] START")
429
429
 
430
430
  # Submit SLURM job and retrieve job ID
431
+ logger.debug(f"[_submit_single_sbatch] Now run {submit_command=}")
432
+ sbatch_stdout = self._run_remote_cmd(submit_command)
431
433
  logger.info(f"[_submit_single_sbatch] {sbatch_stdout=}")
432
434
  stdout = sbatch_stdout.strip("\n")
433
435
  submitted_job_id = int(stdout)
@@ -623,11 +625,19 @@ class BaseSlurmRunner(BaseRunner):
623
625
  )
624
626
 
625
627
  config.parallel_tasks_per_job = 1
626
- self._submit_single_sbatch(
628
+ submit_command = self._prepare_single_slurm_job(
627
629
  base_command=base_command,
628
630
  slurm_job=slurm_job,
629
631
  slurm_config=config,
630
632
  )
633
+ self._send_many_job_inputs(
634
+ workdir_local=workdir_local,
635
+ workdir_remote=workdir_remote,
636
+ )
637
+ self._submit_single_sbatch(
638
+ submit_command=submit_command,
639
+ slurm_job=slurm_job,
640
+ )
631
641
  logger.debug(f"[submit] END submission phase, {self.job_ids=}")
632
642
 
633
643
  create_accounting_record_slurm(
@@ -726,8 +736,8 @@ class BaseSlurmRunner(BaseRunner):
726
736
  status=HistoryUnitStatus.FAILED,
727
737
  db_sync=db,
728
738
  )
729
- results = {}
730
- exceptions = {
739
+ results: dict[int, Any] = {}
740
+ exceptions: dict[int, BaseException] = {
731
741
  ind: SHUTDOWN_EXCEPTION
732
742
  for ind in range(len(list_parameters))
733
743
  }
@@ -801,13 +811,25 @@ class BaseSlurmRunner(BaseRunner):
801
811
  )
802
812
  )
803
813
 
804
- # NOTE: see issue 2431
805
- logger.debug("[multisubmit] Transfer files and submit jobs.")
814
+ submit_commands = []
806
815
  for slurm_job in jobs_to_submit:
816
+ submit_commands.append(
817
+ self._prepare_single_slurm_job(
818
+ base_command=base_command,
819
+ slurm_job=slurm_job,
820
+ slurm_config=config,
821
+ )
822
+ )
823
+ self._send_many_job_inputs(
824
+ workdir_local=workdir_local,
825
+ workdir_remote=workdir_remote,
826
+ )
827
+ for slurm_job, submit_command in zip(
828
+ jobs_to_submit, submit_commands
829
+ ):
807
830
  self._submit_single_sbatch(
808
- base_command=base_command,
831
+ submit_command=submit_command,
809
832
  slurm_job=slurm_job,
810
- slurm_config=config,
811
833
  )
812
834
 
813
835
  logger.info(f"[multisubmit] END submission phase, {self.job_ids=}")
@@ -830,8 +852,10 @@ class BaseSlurmRunner(BaseRunner):
830
852
  status=HistoryUnitStatus.FAILED,
831
853
  db_sync=db,
832
854
  )
833
- results = {}
834
- exceptions = {ind: e for ind in range(len(list_parameters))}
855
+ results: dict[int, Any] = {}
856
+ exceptions: dict[int, BaseException] = {
857
+ ind: e for ind in range(len(list_parameters))
858
+ }
835
859
  return results, exceptions
836
860
 
837
861
  # Retrieval phase
@@ -166,12 +166,69 @@ class SlurmSSHRunner(BaseSlurmRunner):
166
166
  stdout = self.fractal_ssh.run_command(cmd=cmd)
167
167
  return stdout
168
168
 
169
+ def _send_many_job_inputs(
170
+ self, *, workdir_local: Path, workdir_remote: Path
171
+ ) -> None:
172
+ """
173
+ Compress, transfer, and extract a local working directory onto a remote
174
+ host.
175
+
176
+ This method creates a temporary `.tar.gz` archive of the given
177
+ `workdir_local`, transfers it to the remote machine via the configured
178
+ SSH connection, extracts it into `workdir_remote`, and removes the
179
+ temporary archive from both local and remote filesystems.
180
+ """
181
+
182
+ logger.debug("[_send_many_job_inputs] START")
183
+
184
+ tar_path_local = workdir_local.with_suffix(".tar.gz")
185
+ tar_name = Path(tar_path_local).name
186
+ tar_path_remote = workdir_remote.parent / tar_name
187
+
188
+ tar_compression_cmd = get_tar_compression_cmd(
189
+ subfolder_path=workdir_local, filelist_path=None
190
+ )
191
+ _, tar_extraction_cmd = get_tar_extraction_cmd(
192
+ archive_path=tar_path_remote
193
+ )
194
+ rm_tar_cmd = f"rm {tar_path_remote.as_posix()}"
195
+
196
+ try:
197
+ run_subprocess(tar_compression_cmd, logger_name=logger.name)
198
+ logger.debug(
199
+ "[_send_many_job_inputs] "
200
+ f"{workdir_local=} compressed to {tar_path_local=}."
201
+ )
202
+ self.fractal_ssh.send_file(
203
+ local=tar_path_local.as_posix(),
204
+ remote=tar_path_remote.as_posix(),
205
+ )
206
+ logger.debug(
207
+ "[_send_many_job_inputs] "
208
+ f"{tar_path_local=} sent via SSH to {tar_path_remote=}."
209
+ )
210
+ self.fractal_ssh.run_command(cmd=tar_extraction_cmd)
211
+ logger.debug(
212
+ "[_send_many_job_inputs] "
213
+ f"{tar_path_remote=} extracted to {workdir_remote=}."
214
+ )
215
+ self.fractal_ssh.run_command(cmd=rm_tar_cmd)
216
+ logger.debug(
217
+ "[_send_many_job_inputs] "
218
+ f"{tar_path_remote=} removed from remote server."
219
+ )
220
+ except Exception as e:
221
+ raise e
222
+ finally:
223
+ Path(tar_path_local).unlink(missing_ok=True)
224
+ logger.debug(f"[_send_many_job_inputs] {tar_path_local=} removed.")
225
+
226
+ logger.debug("[_send_many_job_inputs] END.")
227
+
169
228
  def run_squeue(
170
229
  self,
171
230
  *,
172
231
  job_ids: list[str],
173
- base_interval: float = 2.0,
174
- max_attempts: int = 7,
175
232
  ) -> str:
176
233
  """
177
234
  Run `squeue` for a set of SLURM job IDs.
@@ -205,8 +262,6 @@ class SlurmSSHRunner(BaseSlurmRunner):
205
262
  try:
206
263
  stdout = self.fractal_ssh.run_command(
207
264
  cmd=cmd,
208
- base_interval=base_interval,
209
- max_attempts=max_attempts,
210
265
  )
211
266
  return stdout
212
267
  except FractalSSHCommandError as e:
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
  def get_tar_compression_cmd(
8
8
  subfolder_path: Path,
9
9
  filelist_path: Path | None,
10
- ) -> tuple[str, str]:
10
+ ) -> str:
11
11
  """
12
12
  Prepare command to compress e.g. `/path/dir` into `/path/dir.tar.gz`.
13
13
 
@@ -17,7 +17,6 @@ def get_tar_compression_cmd(
17
17
  Args:
18
18
  subfolder_path: Absolute path to the folder to compress.
19
19
  filelist_path: If set, to be used in the `--files-from` option.
20
- expected_tarfile: If set, it should match to the returned one.
21
20
 
22
21
  Returns:
23
22
  tar command
@@ -167,7 +167,7 @@ class SudoSlurmRunner(BaseSlurmRunner):
167
167
  )
168
168
  return res.stdout
169
169
 
170
- def run_squeue(self, job_ids: list[str]) -> str:
170
+ def run_squeue(self, *, job_ids: list[str]) -> str:
171
171
  """
172
172
  Run `squeue` for a set of SLURM job IDs.
173
173
  """
@@ -47,7 +47,7 @@ def _remove_status_from_attributes(
47
47
  Drop attribute `IMAGE_STATUS_KEY` from all images.
48
48
  """
49
49
  images_copy = deepcopy(images)
50
- [img["attributes"].pop(IMAGE_STATUS_KEY) for img in images_copy]
50
+ [img["attributes"].pop(IMAGE_STATUS_KEY, None) for img in images_copy]
51
51
  return images_copy
52
52
 
53
53