fractal-server 2.15.1__tar.gz → 2.15.3__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.1 → fractal_server-2.15.3}/PKG-INFO +1 -1
  2. fractal_server-2.15.3/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/db/__init__.py +2 -6
  4. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/job.py +8 -1
  5. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_functions.py +58 -0
  6. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/history.py +62 -23
  7. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/workflow.py +13 -0
  8. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/workflowtask.py +10 -0
  9. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +46 -4
  10. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/remote.py +9 -9
  11. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/_slurm_ssh.py +0 -13
  12. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/ssh/_fabric.py +6 -1
  13. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/collect_pixi.py +1 -1
  14. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/reactivate_pixi.py +1 -1
  15. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/collect_pixi.py +1 -1
  16. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/reactivate_pixi.py +1 -1
  17. {fractal_server-2.15.1 → fractal_server-2.15.3}/pyproject.toml +3 -3
  18. fractal_server-2.15.1/fractal_server/__init__.py +0 -1
  19. {fractal_server-2.15.1 → fractal_server-2.15.3}/LICENSE +0 -0
  20. {fractal_server-2.15.1 → fractal_server-2.15.3}/README.md +0 -0
  21. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/__main__.py +0 -0
  22. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/alembic.ini +0 -0
  23. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/__init__.py +0 -0
  24. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/__init__.py +0 -0
  25. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/linkusergroup.py +0 -0
  26. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/linkuserproject.py +0 -0
  27. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/security.py +0 -0
  28. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/user_settings.py +0 -0
  29. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/__init__.py +0 -0
  30. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/accounting.py +0 -0
  31. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/dataset.py +0 -0
  32. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/history.py +0 -0
  33. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/job.py +0 -0
  34. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/project.py +0 -0
  35. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/task.py +0 -0
  36. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/task_group.py +0 -0
  37. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/workflow.py +0 -0
  38. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/models/v2/workflowtask.py +0 -0
  39. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/__init__.py +0 -0
  40. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/__init__.py +0 -0
  41. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  42. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
  43. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
  44. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/project.py +0 -0
  45. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/task.py +0 -0
  46. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  47. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
  48. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/__init__.py +0 -0
  49. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  50. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_functions_history.py +0 -0
  51. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  52. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_functions_task_version_update.py +0 -0
  53. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
  54. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +0 -0
  55. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  56. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/images.py +0 -0
  57. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/job.py +0 -0
  58. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/pre_submission_checks.py +0 -0
  59. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/project.py +0 -0
  60. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
  61. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/submit.py +0 -0
  62. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task.py +0 -0
  63. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  64. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  65. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_collection_pixi.py +0 -0
  66. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  67. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
  68. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/task_version_update.py +0 -0
  69. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  70. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/__init__.py +0 -0
  71. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  72. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/current_user.py +0 -0
  73. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/group.py +0 -0
  74. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/login.py +0 -0
  75. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/oauth.py +0 -0
  76. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/register.py +0 -0
  77. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/router.py +0 -0
  78. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/auth/users.py +0 -0
  79. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/aux/__init__.py +0 -0
  80. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/aux/_job.py +0 -0
  81. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/aux/_runner.py +0 -0
  82. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  83. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/routes/pagination.py +0 -0
  84. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/__init__.py +0 -0
  85. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/components.py +0 -0
  86. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/exceptions.py +0 -0
  87. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/__init__.py +0 -0
  88. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/base_runner.py +0 -0
  89. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/call_command_wrapper.py +0 -0
  90. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/local/__init__.py +0 -0
  91. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/local/get_local_config.py +0 -0
  92. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/local/runner.py +0 -0
  93. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/__init__.py +0 -0
  94. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/_batching.py +0 -0
  95. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/_job_states.py +0 -0
  96. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/_slurm_config.py +0 -0
  97. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +0 -0
  98. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +0 -0
  99. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_ssh/__init__.py +0 -0
  100. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_ssh/run_subprocess.py +0 -0
  101. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_ssh/runner.py +0 -0
  102. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_ssh/tar_commands.py +0 -0
  103. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
  104. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +0 -0
  105. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/executors/slurm_sudo/runner.py +0 -0
  106. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/filenames.py +0 -0
  107. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  108. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/shutdown.py +0 -0
  109. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/task_files.py +0 -0
  110. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/__init__.py +0 -0
  111. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/_local.py +0 -0
  112. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/_slurm_sudo.py +0 -0
  113. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/db_tools.py +0 -0
  114. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  115. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  116. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/runner.py +0 -0
  117. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  118. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/submit_workflow.py +0 -0
  119. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/v2/task_interface.py +0 -0
  120. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/runner/versions.py +0 -0
  121. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/__init__.py +0 -0
  122. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/user.py +0 -0
  123. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/user_group.py +0 -0
  124. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/user_settings.py +0 -0
  125. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/__init__.py +0 -0
  126. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/accounting.py +0 -0
  127. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/dataset.py +0 -0
  128. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/dumps.py +0 -0
  129. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/history.py +0 -0
  130. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/job.py +0 -0
  131. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/manifest.py +0 -0
  132. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/project.py +0 -0
  133. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
  134. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/task.py +0 -0
  135. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  136. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/task_group.py +0 -0
  137. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/workflow.py +0 -0
  138. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  139. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/security/__init__.py +0 -0
  140. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/security/signup_email.py +0 -0
  141. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/app/user_settings.py +0 -0
  142. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/config.py +0 -0
  143. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/data_migrations/2_14_10.py +0 -0
  144. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/data_migrations/README.md +0 -0
  145. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/data_migrations/tools.py +0 -0
  146. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/exceptions.py +0 -0
  147. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/gunicorn_fractal.py +0 -0
  148. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/images/__init__.py +0 -0
  149. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/images/models.py +0 -0
  150. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/images/status_tools.py +0 -0
  151. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/images/tools.py +0 -0
  152. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/logger.py +0 -0
  153. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/main.py +0 -0
  154. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/env.py +0 -0
  155. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/naming_convention.py +0 -0
  156. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  157. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  158. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  159. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
  160. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
  161. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +0 -0
  162. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  163. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  164. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  165. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  166. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  167. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  168. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  169. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/791ce783d3d8_add_indices.py +0 -0
  170. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  171. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  172. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  173. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  174. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +0 -0
  175. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  176. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  177. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  178. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +0 -0
  179. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  180. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  181. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
  182. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
  183. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +0 -0
  184. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +0 -0
  185. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +0 -0
  186. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  187. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  188. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  189. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
  190. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  191. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +0 -0
  192. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  193. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +0 -0
  194. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  195. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
  196. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/py.typed +0 -0
  197. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/ssh/__init__.py +0 -0
  198. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/string_tools.py +0 -0
  199. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/syringe.py +0 -0
  200. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/__init__.py +0 -0
  201. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/utils.py +0 -0
  202. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/__init__.py +0 -0
  203. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/__init__.py +0 -0
  204. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/_utils.py +0 -0
  205. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/collect.py +0 -0
  206. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/deactivate.py +0 -0
  207. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/deactivate_pixi.py +0 -0
  208. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/local/reactivate.py +0 -0
  209. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  210. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
  211. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/collect.py +0 -0
  212. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
  213. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/deactivate_pixi.py +0 -0
  214. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
  215. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  216. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  217. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  218. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  219. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  220. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  221. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/pixi_1_extract.sh +0 -0
  222. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/pixi_2_install.sh +0 -0
  223. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/templates/pixi_3_post_install.sh +0 -0
  224. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_background.py +0 -0
  225. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_database.py +0 -0
  226. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  227. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_pixi.py +0 -0
  228. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
  229. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/tasks/v2/utils_templates.py +0 -0
  230. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/types/__init__.py +0 -0
  231. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/types/validators/__init__.py +0 -0
  232. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/types/validators/_common_validators.py +0 -0
  233. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/types/validators/_filter_validators.py +0 -0
  234. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/types/validators/_workflow_task_arguments_validators.py +0 -0
  235. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/urls.py +0 -0
  236. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/utils.py +0 -0
  237. {fractal_server-2.15.1 → fractal_server-2.15.3}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.15.1
3
+ Version: 2.15.3
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License: BSD-3-Clause
6
6
  Author: Tommaso Comparin
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.15.3"
@@ -45,13 +45,11 @@ class DB:
45
45
  settings = Inject(get_settings)
46
46
  settings.check_db()
47
47
 
48
- engine_kwargs_async = {"pool_pre_ping": True}
49
-
50
48
  cls._engine_async = create_async_engine(
51
49
  settings.DATABASE_ASYNC_URL,
52
50
  echo=settings.DB_ECHO,
53
51
  future=True,
54
- **engine_kwargs_async,
52
+ pool_pre_ping=True,
55
53
  )
56
54
  cls._async_session_maker = sessionmaker(
57
55
  cls._engine_async,
@@ -65,13 +63,11 @@ class DB:
65
63
  settings = Inject(get_settings)
66
64
  settings.check_db()
67
65
 
68
- engine_kwargs_sync = {}
69
-
70
66
  cls._engine_sync = create_engine(
71
67
  settings.DATABASE_SYNC_URL,
72
68
  echo=settings.DB_ECHO,
73
69
  future=True,
74
- **engine_kwargs_sync,
70
+ pool_pre_ping=True,
75
71
  )
76
72
 
77
73
  cls._sync_session_maker = sessionmaker(
@@ -156,8 +156,15 @@ async def update_job(
156
156
  detail=f"Cannot set job status to {job_update.status}",
157
157
  )
158
158
 
159
+ timestamp = get_timestamp()
159
160
  setattr(job, "status", job_update.status)
160
- setattr(job, "end_timestamp", get_timestamp())
161
+ setattr(job, "end_timestamp", timestamp)
162
+ setattr(
163
+ job,
164
+ "log",
165
+ f"{job.log or ''}\nThis job was manually marked as "
166
+ f"'{JobStatusTypeV2.FAILED}' by an admin ({timestamp.isoformat()}).",
167
+ )
161
168
  await db.commit()
162
169
  await db.refresh(job)
163
170
  await db.close()
@@ -6,6 +6,7 @@ from typing import Literal
6
6
 
7
7
  from fastapi import HTTPException
8
8
  from fastapi import status
9
+ from sqlalchemy.exc import MultipleResultsFound
9
10
  from sqlalchemy.orm.attributes import flag_modified
10
11
  from sqlmodel import select
11
12
  from sqlmodel.sql.expression import SelectOfScalar
@@ -19,6 +20,9 @@ from ....models.v2 import TaskV2
19
20
  from ....models.v2 import WorkflowTaskV2
20
21
  from ....models.v2 import WorkflowV2
21
22
  from ....schemas.v2 import JobStatusTypeV2
23
+ from fractal_server.logger import set_logger
24
+
25
+ logger = set_logger(__name__)
22
26
 
23
27
 
24
28
  async def _get_project_check_owner(
@@ -325,6 +329,24 @@ def _get_submitted_jobs_statement() -> SelectOfScalar:
325
329
  return stm
326
330
 
327
331
 
332
+ async def _workflow_has_submitted_job(
333
+ workflow_id: int,
334
+ db: AsyncSession,
335
+ ) -> bool:
336
+
337
+ res = await db.execute(
338
+ select(JobV2.id)
339
+ .where(JobV2.status == JobStatusTypeV2.SUBMITTED)
340
+ .where(JobV2.workflow_id == workflow_id)
341
+ .limit(1)
342
+ )
343
+ submitted_jobs = res.scalar_one_or_none()
344
+ if submitted_jobs is not None:
345
+ return True
346
+
347
+ return False
348
+
349
+
328
350
  async def _workflow_insert_task(
329
351
  *,
330
352
  workflow_id: int,
@@ -481,3 +503,39 @@ async def _get_workflowtask_or_404(
481
503
  )
482
504
  else:
483
505
  return wftask
506
+
507
+
508
+ async def _get_submitted_job_or_none(
509
+ *,
510
+ dataset_id: int,
511
+ workflow_id: int,
512
+ db: AsyncSession,
513
+ ) -> JobV2 | None:
514
+ """
515
+ Get the submitted job for given dataset/workflow, if any.
516
+
517
+ This function also handles the invalid branch where more than one job
518
+ is found.
519
+
520
+ Args:
521
+ dataset_id:
522
+ workflow_id:
523
+ db:
524
+ """
525
+ res = await db.execute(
526
+ _get_submitted_jobs_statement()
527
+ .where(JobV2.dataset_id == dataset_id)
528
+ .where(JobV2.workflow_id == workflow_id)
529
+ )
530
+ try:
531
+ return res.scalars().one_or_none()
532
+ except MultipleResultsFound as e:
533
+ error_msg = (
534
+ "Multiple running jobs found for "
535
+ f"{dataset_id=} and {workflow_id=}."
536
+ )
537
+ logger.error(f"{error_msg} Original error: {str(e)}.")
538
+ raise HTTPException(
539
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
540
+ detail=error_msg,
541
+ )
@@ -1,5 +1,3 @@
1
- from copy import deepcopy
2
-
3
1
  from fastapi import APIRouter
4
2
  from fastapi import Depends
5
3
  from fastapi import HTTPException
@@ -9,6 +7,7 @@ from sqlmodel import func
9
7
  from sqlmodel import select
10
8
 
11
9
  from ._aux_functions import _get_dataset_check_owner
10
+ from ._aux_functions import _get_submitted_job_or_none
12
11
  from ._aux_functions import _get_workflow_check_owner
13
12
  from ._aux_functions_history import _verify_workflow_and_dataset_access
14
13
  from ._aux_functions_history import get_history_run_or_404
@@ -72,6 +71,7 @@ async def get_workflow_tasks_statuses(
72
71
  user: UserOAuth = Depends(current_active_user),
73
72
  db: AsyncSession = Depends(get_async_db),
74
73
  ) -> JSONResponse:
74
+
75
75
  # Access control
76
76
  workflow = await _get_workflow_check_owner(
77
77
  project_id=project_id,
@@ -86,6 +86,19 @@ async def get_workflow_tasks_statuses(
86
86
  db=db,
87
87
  )
88
88
 
89
+ running_job = await _get_submitted_job_or_none(
90
+ db=db,
91
+ dataset_id=dataset_id,
92
+ workflow_id=workflow_id,
93
+ )
94
+ if running_job is not None:
95
+ running_wftasks = workflow.task_list[
96
+ running_job.first_task_index : running_job.last_task_index + 1
97
+ ]
98
+ running_wftask_ids = [wft.id for wft in running_wftasks]
99
+ else:
100
+ running_wftask_ids = []
101
+
89
102
  response: dict[int, dict[str, int | str] | None] = {}
90
103
  for wftask in workflow.task_list:
91
104
  res = await db.execute(
@@ -95,17 +108,37 @@ async def get_workflow_tasks_statuses(
95
108
  .order_by(HistoryRun.timestamp_started.desc())
96
109
  .limit(1)
97
110
  )
98
- latest_history_run = res.scalar_one_or_none()
99
- if latest_history_run is None:
100
- logger.debug(
101
- f"No HistoryRun found for {dataset_id=} and {wftask.id=}."
102
- )
103
- response[wftask.id] = None
111
+ latest_run = res.scalar_one_or_none()
112
+
113
+ if latest_run is None:
114
+ if wftask.id in running_wftask_ids:
115
+ logger.debug(f"A1: No HistoryRun for {wftask.id=}.")
116
+ response[wftask.id] = dict(status=HistoryUnitStatus.SUBMITTED)
117
+ else:
118
+ logger.debug(f"A2: No HistoryRun for {wftask.id=}.")
119
+ response[wftask.id] = None
104
120
  continue
105
- response[wftask.id] = dict(
106
- status=latest_history_run.status,
107
- num_available_images=latest_history_run.num_available_images,
108
- )
121
+ else:
122
+ if wftask.id in running_wftask_ids:
123
+ if latest_run.job_id == running_job.id:
124
+ logger.debug(
125
+ f"B1 for {wftask.id} and {latest_run.job_id=}."
126
+ )
127
+ response[wftask.id] = dict(status=latest_run.status)
128
+ else:
129
+ logger.debug(
130
+ f"B2 for {wftask.id} and {latest_run.job_id=}."
131
+ )
132
+ response[wftask.id] = dict(
133
+ status=HistoryUnitStatus.SUBMITTED
134
+ )
135
+ else:
136
+ logger.debug(f"C1: {wftask.id=} not in {running_wftask_ids=}.")
137
+ response[wftask.id] = dict(status=latest_run.status)
138
+
139
+ response[wftask.id][
140
+ "num_available_images"
141
+ ] = latest_run.num_available_images
109
142
 
110
143
  for target_status in HistoryUnitStatus:
111
144
  stm = (
@@ -122,18 +155,24 @@ async def get_workflow_tasks_statuses(
122
155
  num_images = res.scalar()
123
156
  response[wftask.id][f"num_{target_status}_images"] = num_images
124
157
 
125
- new_response = deepcopy(response)
126
- for key, value in response.items():
127
- if value is not None:
128
- num_total_images = sum(
129
- value[f"num_{target_status}_images"]
130
- for target_status in HistoryUnitStatus
131
- )
132
- if num_total_images > value["num_available_images"]:
133
- value["num_available_images"] = None
134
- new_response[key] = value
158
+ # Set `num_available_images=None` for cases where it would be
159
+ # smaller than `num_total_images`
160
+ values_to_skip = (None, {"status": HistoryUnitStatus.SUBMITTED})
161
+ response_update = {}
162
+ for wftask_id, status_value in response.items():
163
+ if status_value in values_to_skip:
164
+ # Skip cases where status has no image counters
165
+ continue
166
+ num_total_images = sum(
167
+ status_value[f"num_{target_status}_images"]
168
+ for target_status in HistoryUnitStatus
169
+ )
170
+ if num_total_images > status_value["num_available_images"]:
171
+ status_value["num_available_images"] = None
172
+ response_update[wftask_id] = status_value
173
+ response.update(response_update)
135
174
 
136
- return JSONResponse(content=new_response, status_code=200)
175
+ return JSONResponse(content=response, status_code=200)
137
176
 
138
177
 
139
178
  @router.get("/project/{project_id}/status/run/")
@@ -22,6 +22,7 @@ from ._aux_functions import _check_workflow_exists
22
22
  from ._aux_functions import _get_project_check_owner
23
23
  from ._aux_functions import _get_submitted_jobs_statement
24
24
  from ._aux_functions import _get_workflow_check_owner
25
+ from ._aux_functions import _workflow_has_submitted_job
25
26
  from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
26
27
  from fractal_server.app.models import UserOAuth
27
28
  from fractal_server.app.models.v2 import TaskGroupV2
@@ -146,6 +147,18 @@ async def update_workflow(
146
147
 
147
148
  for key, value in patch.model_dump(exclude_unset=True).items():
148
149
  if key == "reordered_workflowtask_ids":
150
+
151
+ if await _workflow_has_submitted_job(
152
+ workflow_id=workflow_id, db=db
153
+ ):
154
+ raise HTTPException(
155
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
156
+ detail=(
157
+ "Cannot re-order WorkflowTasks while a Job is running "
158
+ "for this Workflow."
159
+ ),
160
+ )
161
+
149
162
  current_workflowtask_ids = [
150
163
  wftask.id for wftask in workflow.task_list
151
164
  ]
@@ -10,6 +10,7 @@ from ....db import AsyncSession
10
10
  from ....db import get_async_db
11
11
  from ._aux_functions import _get_workflow_check_owner
12
12
  from ._aux_functions import _get_workflow_task_check_owner
13
+ from ._aux_functions import _workflow_has_submitted_job
13
14
  from ._aux_functions import _workflow_insert_task
14
15
  from ._aux_functions_tasks import _check_type_filters_compatibility
15
16
  from ._aux_functions_tasks import _get_task_read_access
@@ -224,6 +225,15 @@ async def delete_workflowtask(
224
225
  db=db,
225
226
  )
226
227
 
228
+ if await _workflow_has_submitted_job(workflow_id=workflow_id, db=db):
229
+ raise HTTPException(
230
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
231
+ detail=(
232
+ "Cannot delete a WorkflowTask while a Job is running for this "
233
+ "Workflow."
234
+ ),
235
+ )
236
+
227
237
  # Delete WorkflowTask
228
238
  await db.delete(db_workflow_task)
229
239
  await db.commit()
@@ -137,6 +137,34 @@ 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:
141
+ """
142
+ Determine whether a `squeue` error is considered recoverable.
143
+
144
+ A _recoverable_ error is one which will disappear after some time,
145
+ without any specific action from the `fractal-server` side.
146
+
147
+ Note: if this function returns `True` for an error that does not
148
+ actually recover, this leads to an infinite loop where
149
+ `fractal-server` keeps polling `squeue` information forever.
150
+
151
+ More info at
152
+ https://github.com/fractal-analytics-platform/fractal-server/issues/2682
153
+
154
+ Args:
155
+ exception: The exception raised by `self.run_squeue`.
156
+ Returns:
157
+ Whether the error is considered recoverable.
158
+ """
159
+ str_exception = str(exception)
160
+ if (
161
+ "slurm_load_jobs" in str_exception
162
+ and "Socket timed out on send/recv operation" in str_exception
163
+ ):
164
+ return True
165
+ else:
166
+ return False
167
+
140
168
  def _get_finished_jobs(self, job_ids: list[str]) -> set[str]:
141
169
  # If there is no Slurm job to check, return right away
142
170
  if not job_ids:
@@ -161,12 +189,26 @@ class BaseSlurmRunner(BaseRunner):
161
189
  {stdout.split()[0]: stdout.split()[1]}
162
190
  )
163
191
  except Exception as e:
164
- logger.warning(
165
- "[_get_finished_jobs] `squeue` failed for "
166
- f"{job_id=}, mark job as completed. "
192
+ msg = (
193
+ f"[_get_finished_jobs] `squeue` failed for {job_id=}. "
167
194
  f"Original error: {str(e)}."
168
195
  )
169
- slurm_statuses.update({str(job_id): "COMPLETED"})
196
+ logger.warning(msg)
197
+ if self._is_squeue_error_recoverable(e):
198
+ logger.warning(
199
+ "[_get_finished_jobs] Recoverable `squeue` "
200
+ f"error - mark {job_id=} as FRACTAL_UNDEFINED and"
201
+ " retry later."
202
+ )
203
+ slurm_statuses.update(
204
+ {str(job_id): "FRACTAL_UNDEFINED"}
205
+ )
206
+ else:
207
+ logger.warning(
208
+ "[_get_finished_jobs] Non-recoverable `squeue`"
209
+ f"error - mark {job_id=} as completed."
210
+ )
211
+ slurm_statuses.update({str(job_id): "COMPLETED"})
170
212
 
171
213
  # If a job is not in `squeue` output, mark it as completed.
172
214
  finished_jobs = {
@@ -1,6 +1,5 @@
1
1
  import argparse
2
2
  import json
3
- import logging
4
3
  import os
5
4
  import sys
6
5
 
@@ -32,7 +31,6 @@ def worker(
32
31
  # Create output folder, if missing
33
32
  out_dir = os.path.dirname(out_fname)
34
33
  if not os.path.exists(out_dir):
35
- logging.debug(f"_slurm.remote.worker: create {out_dir=}")
36
34
  os.mkdir(out_dir)
37
35
 
38
36
  # Execute the job and capture exceptions
@@ -40,10 +38,8 @@ def worker(
40
38
  with open(in_fname) as f:
41
39
  input_data = json.load(f)
42
40
 
43
- server_python_version = input_data["python_version"]
44
- server_fractal_server_version = input_data["fractal_server_version"]
45
-
46
41
  # Fractal-server version must be identical
42
+ server_fractal_server_version = input_data["fractal_server_version"]
47
43
  worker_fractal_server_version = __VERSION__
48
44
  if worker_fractal_server_version != server_fractal_server_version:
49
45
  raise FractalVersionMismatch(
@@ -51,11 +47,16 @@ def worker(
51
47
  f"{worker_fractal_server_version=}"
52
48
  )
53
49
 
54
- # Python version mismatch only raises a warning
55
- worker_python_version = tuple(sys.version_info[:3])
50
+ # Get `worker_python_version` as a `list` since this is the type of
51
+ # `server_python_version` after a JSON dump/load round trip.
52
+ worker_python_version = list(sys.version_info[:3])
53
+
54
+ # Print a warning for Python version mismatch
55
+ server_python_version = input_data["python_version"]
56
56
  if worker_python_version != server_python_version:
57
57
  if worker_python_version[:2] != server_python_version[:2]:
58
- logging.warning(
58
+ print(
59
+ "WARNING: "
59
60
  f"{server_python_version=} but {worker_python_version=}."
60
61
  )
61
62
 
@@ -116,7 +117,6 @@ if __name__ == "__main__":
116
117
  required=True,
117
118
  )
118
119
  parsed_args = parser.parse_args()
119
- logging.debug(f"{parsed_args=}")
120
120
 
121
121
  kwargs = dict(
122
122
  in_fname=parsed_args.input_file,
@@ -20,7 +20,6 @@ from pathlib import Path
20
20
  from ....ssh._fabric import FractalSSH
21
21
  from ...models.v2 import DatasetV2
22
22
  from ...models.v2 import WorkflowV2
23
- from ..exceptions import JobExecutionError
24
23
  from ..executors.slurm_common.get_slurm_config import get_slurm_config
25
24
  from ..executors.slurm_ssh.runner import SlurmSSHRunner
26
25
  from ..set_start_and_last_task_index import set_start_and_last_task_index
@@ -64,18 +63,6 @@ def process_workflow(
64
63
  if isinstance(worker_init, str):
65
64
  worker_init = worker_init.split("\n")
66
65
 
67
- # Create main remote folder
68
- try:
69
- fractal_ssh.mkdir(folder=str(workflow_dir_remote))
70
- logger.info(f"Created {str(workflow_dir_remote)} via SSH.")
71
- except Exception as e:
72
- error_msg = (
73
- f"Could not create {str(workflow_dir_remote)} via SSH.\n"
74
- f"Original error: {str(e)}."
75
- )
76
- logger.error(error_msg)
77
- raise JobExecutionError(info=error_msg)
78
-
79
66
  with SlurmSSHRunner(
80
67
  fractal_ssh=fractal_ssh,
81
68
  root_dir_local=workflow_dir_local,
@@ -165,7 +165,11 @@ class FractalSSH:
165
165
  raise e
166
166
 
167
167
  def _run(
168
- self, *args, label: str, lock_timeout: float | None = None, **kwargs
168
+ self,
169
+ *args,
170
+ label: str,
171
+ lock_timeout: float | None = None,
172
+ **kwargs,
169
173
  ) -> Any:
170
174
  actual_lock_timeout = self.default_lock_timeout
171
175
  if lock_timeout is not None:
@@ -353,6 +357,7 @@ class FractalSSH:
353
357
  label=f"run {cmd}",
354
358
  lock_timeout=actual_lock_timeout,
355
359
  hide=True,
360
+ in_stream=False,
356
361
  )
357
362
  t_1 = time.perf_counter()
358
363
  self.logger.info(
@@ -161,7 +161,7 @@ def collect_local_pixi(
161
161
 
162
162
  # Make task folder 755
163
163
  source_dir = Path(task_group.path, SOURCE_DIR_NAME).as_posix()
164
- command = f"chmod 755 {source_dir} -R"
164
+ command = f"chmod -R 755 {source_dir}"
165
165
  execute_command_sync(
166
166
  command=command,
167
167
  logger_name=LOGGER_NAME,
@@ -145,7 +145,7 @@ def reactivate_local_pixi(
145
145
 
146
146
  # Make task folder 755
147
147
  source_dir = Path(task_group.path, SOURCE_DIR_NAME).as_posix()
148
- command = f"chmod 755 {source_dir} -R"
148
+ command = f"chmod -R 755 {source_dir}"
149
149
  execute_command_sync(
150
150
  command=command,
151
151
  logger_name=LOGGER_NAME,
@@ -221,7 +221,7 @@ def collect_ssh_pixi(
221
221
  source_dir = Path(
222
222
  task_group.path, SOURCE_DIR_NAME
223
223
  ).as_posix()
224
- fractal_ssh.run_command(cmd=f"chmod 755 {source_dir} -R")
224
+ fractal_ssh.run_command(cmd=f"chmod -R 755 {source_dir}")
225
225
 
226
226
  # Read and validate remote manifest file
227
227
  manifest_path_remote = (
@@ -196,7 +196,7 @@ def reactivate_ssh_pixi(
196
196
  activity.log = get_current_log(log_file_path)
197
197
  activity = add_commit_refresh(obj=activity, db=db)
198
198
 
199
- fractal_ssh.run_command(cmd=f"chmod 755 {source_dir} -R")
199
+ fractal_ssh.run_command(cmd=f"chmod -R 755 {source_dir}")
200
200
 
201
201
  # Finalize (write metadata to DB)
202
202
  activity.status = TaskGroupActivityStatusV2.OK
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fractal-server"
3
- version = "2.15.1"
3
+ version = "2.15.3"
4
4
  description = "Backend component of the Fractal analytics platform"
5
5
  authors = [
6
6
  { name="Tommaso Comparin", email="tommaso.comparin@exact-lab.it" },
@@ -91,10 +91,10 @@ filterwarnings = [
91
91
  "error::RuntimeWarning",
92
92
  "error::pytest.PytestUnraisableExceptionWarning",
93
93
  ]
94
- markers = ["container", "ssh"]
94
+ markers = ["container", "ssh", "fails_on_macos"]
95
95
 
96
96
  [tool.bumpver]
97
- current_version = "2.15.1"
97
+ current_version = "2.15.3"
98
98
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
99
99
  commit_message = "bump version {old_version} -> {new_version}"
100
100
  commit = true
@@ -1 +0,0 @@
1
- __VERSION__ = "2.15.1"
File without changes