fractal-server 2.7.0a8__tar.gz → 2.7.0a10__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 (230) hide show
  1. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/PKG-INFO +1 -1
  2. fractal_server-2.7.0a10/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +1 -1
  4. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/submit.py +0 -32
  5. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/task_group.py +7 -0
  6. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/ssh/executor.py +10 -12
  7. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/ssh/_fabric.py +186 -73
  8. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/background_operations_ssh.py +15 -9
  9. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/pyproject.toml +2 -2
  10. fractal_server-2.7.0a8/fractal_server/__init__.py +0 -1
  11. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/LICENSE +0 -0
  12. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/README.md +0 -0
  13. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/__main__.py +0 -0
  14. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/alembic.ini +0 -0
  15. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/__init__.py +0 -0
  16. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/db/__init__.py +0 -0
  17. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/__init__.py +0 -0
  18. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/linkusergroup.py +0 -0
  19. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/linkuserproject.py +0 -0
  20. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/security.py +0 -0
  21. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/user_settings.py +0 -0
  22. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/__init__.py +0 -0
  23. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/dataset.py +0 -0
  24. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/job.py +0 -0
  25. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/project.py +0 -0
  26. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/state.py +0 -0
  27. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/task.py +0 -0
  28. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v1/workflow.py +0 -0
  29. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/__init__.py +0 -0
  30. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/collection_state.py +0 -0
  31. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/dataset.py +0 -0
  32. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/job.py +0 -0
  33. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/project.py +0 -0
  34. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/task.py +0 -0
  35. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/workflow.py +0 -0
  36. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/models/v2/workflowtask.py +0 -0
  37. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/__init__.py +0 -0
  38. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/__init__.py +0 -0
  39. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v1.py +0 -0
  40. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  41. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v2/job.py +0 -0
  42. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v2/project.py +0 -0
  43. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v2/task.py +0 -0
  44. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  45. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/__init__.py +0 -0
  46. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  47. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  48. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  49. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/job.py +0 -0
  50. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/project.py +0 -0
  51. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/task.py +0 -0
  52. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
  53. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  54. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  55. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  56. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  57. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  58. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/images.py +0 -0
  59. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/job.py +0 -0
  60. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/project.py +0 -0
  61. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/status.py +0 -0
  62. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/task.py +0 -0
  63. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  64. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  65. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  66. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  67. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  68. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/__init__.py +0 -0
  69. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  70. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/current_user.py +0 -0
  71. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/group.py +0 -0
  72. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/login.py +0 -0
  73. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/oauth.py +0 -0
  74. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/register.py +0 -0
  75. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/router.py +0 -0
  76. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/auth/users.py +0 -0
  77. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/aux/__init__.py +0 -0
  78. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/aux/_job.py +0 -0
  79. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/aux/_runner.py +0 -0
  80. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  81. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/.gitignore +0 -0
  82. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/__init__.py +0 -0
  83. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/async_wrap.py +0 -0
  84. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/components.py +0 -0
  85. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/compress_folder.py +0 -0
  86. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/exceptions.py +0 -0
  87. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/__init__.py +0 -0
  88. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  89. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  90. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  91. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  92. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  93. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  94. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  95. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  96. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  97. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  98. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  99. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
  100. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/extract_archive.py +0 -0
  101. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/filenames.py +0 -0
  102. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/run_subprocess.py +0 -0
  103. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  104. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/shutdown.py +0 -0
  105. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/task_files.py +0 -0
  106. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/__init__.py +0 -0
  107. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_common.py +0 -0
  108. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  109. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  110. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  111. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  112. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  113. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  114. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  115. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/common.py +0 -0
  116. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  117. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/__init__.py +0 -0
  118. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  119. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  120. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  121. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  122. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  123. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  124. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  125. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  126. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  127. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  128. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  129. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  130. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  131. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  132. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  133. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  134. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  135. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/runner.py +0 -0
  136. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  137. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  138. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/v2/task_interface.py +0 -0
  139. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/runner/versions.py +0 -0
  140. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/__init__.py +0 -0
  141. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/_validators.py +0 -0
  142. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/user.py +0 -0
  143. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/user_group.py +0 -0
  144. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/user_settings.py +0 -0
  145. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/__init__.py +0 -0
  146. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  147. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/dataset.py +0 -0
  148. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/dumps.py +0 -0
  149. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/manifest.py +0 -0
  150. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/project.py +0 -0
  151. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/state.py +0 -0
  152. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/task.py +0 -0
  153. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  154. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v1/workflow.py +0 -0
  155. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/__init__.py +0 -0
  156. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/dataset.py +0 -0
  157. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/dumps.py +0 -0
  158. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/job.py +0 -0
  159. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/manifest.py +0 -0
  160. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/project.py +0 -0
  161. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/status.py +0 -0
  162. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/task.py +0 -0
  163. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  164. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/task_group.py +0 -0
  165. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/workflow.py +0 -0
  166. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  167. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/security/__init__.py +0 -0
  168. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/app/user_settings.py +0 -0
  169. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/config.py +0 -0
  170. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/data_migrations/2_7_0.py +0 -0
  171. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/data_migrations/README.md +0 -0
  172. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/data_migrations/tools.py +0 -0
  173. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/gunicorn_fractal.py +0 -0
  174. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/images/__init__.py +0 -0
  175. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/images/models.py +0 -0
  176. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/images/tools.py +0 -0
  177. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/logger.py +0 -0
  178. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/main.py +0 -0
  179. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/README +0 -0
  180. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/env.py +0 -0
  181. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/naming_convention.py +0 -0
  182. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/script.py.mako +0 -0
  183. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  184. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  185. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  186. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  187. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  188. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  189. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  190. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  191. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  192. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  193. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  194. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  195. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  196. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  197. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  198. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  199. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  200. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  201. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  202. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  203. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  204. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  205. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/py.typed +0 -0
  206. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/ssh/__init__.py +0 -0
  207. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/string_tools.py +0 -0
  208. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/syringe.py +0 -0
  209. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/__init__.py +0 -0
  210. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/utils.py +0 -0
  211. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
  212. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/__init__.py +0 -0
  213. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/background_operations.py +0 -0
  214. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  215. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/get_collection_data.py +0 -0
  216. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v1/utils.py +0 -0
  217. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/__init__.py +0 -0
  218. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/_venv_pip.py +0 -0
  219. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/background_operations.py +0 -0
  220. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/database_operations.py +0 -0
  221. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/endpoint_operations.py +0 -0
  222. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -0
  223. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +0 -0
  224. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/templates/_3_pip_install.sh +0 -0
  225. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/templates/_4_pip_freeze.sh +0 -0
  226. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/templates/_5_pip_show.sh +0 -0
  227. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/tasks/v2/utils.py +0 -0
  228. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/urls.py +0 -0
  229. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/utils.py +0 -0
  230. {fractal_server-2.7.0a8 → fractal_server-2.7.0a10}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.7.0a8
3
+ Version: 2.7.0a10
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.7.0a10"
@@ -182,7 +182,7 @@ async def _get_task_read_access(
182
182
  if not task_group.active:
183
183
  raise HTTPException(
184
184
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
185
- detail="Cannot insert non-active tasks into a workflow.",
185
+ detail=f"Error: task {task_id} ({task.name}) is not active.",
186
186
  )
187
187
  return task
188
188
 
@@ -1,6 +1,5 @@
1
1
  import os
2
2
  from datetime import datetime
3
- from datetime import timedelta
4
3
  from datetime import timezone
5
4
  from pathlib import Path
6
5
  from typing import Optional
@@ -181,37 +180,6 @@ async def apply_workflow(
181
180
  **job_create.dict(),
182
181
  )
183
182
 
184
- # Rate Limiting:
185
- # raise `429 TOO MANY REQUESTS` if this endpoint has been called with the
186
- # same database keys (Project, Workflow and Datasets) during the last
187
- # `settings.FRACTAL_API_SUBMIT_RATE_LIMIT` seconds.
188
- stm = (
189
- select(JobV2)
190
- .where(JobV2.project_id == project_id)
191
- .where(JobV2.workflow_id == workflow_id)
192
- .where(JobV2.dataset_id == dataset_id)
193
- )
194
- res = await db.execute(stm)
195
- db_jobs = res.scalars().all()
196
- if db_jobs and any(
197
- abs(
198
- job.start_timestamp
199
- - db_job.start_timestamp.replace(tzinfo=timezone.utc)
200
- )
201
- < timedelta(seconds=settings.FRACTAL_API_SUBMIT_RATE_LIMIT)
202
- for db_job in db_jobs
203
- ):
204
- raise HTTPException(
205
- status_code=status.HTTP_429_TOO_MANY_REQUESTS,
206
- detail=(
207
- f"The endpoint 'POST /api/v2/project/{project_id}/job/submit/'"
208
- " was called several times within an interval of less "
209
- f"than {settings.FRACTAL_API_SUBMIT_RATE_LIMIT} seconds, using"
210
- " the same foreign keys. If it was intentional, please wait "
211
- "and try again."
212
- ),
213
- )
214
-
215
183
  db.add(job)
216
184
  await db.commit()
217
185
  await db.refresh(job)
@@ -35,6 +35,7 @@ async def get_task_group_list(
35
35
  db: AsyncSession = Depends(get_async_db),
36
36
  only_active: bool = False,
37
37
  only_owner: bool = False,
38
+ args_schema: bool = True,
38
39
  ) -> list[TaskGroupReadV2]:
39
40
  """
40
41
  Get all accessible TaskGroups
@@ -58,6 +59,12 @@ async def get_task_group_list(
58
59
  res = await db.execute(stm)
59
60
  task_groups = res.scalars().all()
60
61
 
62
+ if args_schema is False:
63
+ for taskgroup in task_groups:
64
+ for task in taskgroup.task_list:
65
+ setattr(task, "args_schema_non_parallel", None)
66
+ setattr(task, "args_schema_parallel", None)
67
+
61
68
  return task_groups
62
69
 
63
70
 
@@ -861,7 +861,7 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
861
861
 
862
862
  # Transfer archive
863
863
  t_0_put = time.perf_counter()
864
- self.fractal_ssh.put(
864
+ self.fractal_ssh.send_file(
865
865
  local=tarfile_path_local,
866
866
  remote=tarfile_path_remote,
867
867
  )
@@ -1255,16 +1255,9 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1255
1255
  self.workflow_dir_remote / f"{subfolder_name}.tar.gz"
1256
1256
  ).as_posix()
1257
1257
 
1258
- # Remove local tarfile - FIXME SSH: is this needed?
1259
- logger.warning(f"In principle I just removed {tarfile_path_local}")
1260
- logger.warning(f"{Path(tarfile_path_local).exists()=}")
1261
-
1262
- # Remove remote tarfile - FIXME SSH: is this needed?
1263
- # rm_command = f"rm {tarfile_path_remote}"
1264
- # _run_command_over_ssh(cmd=rm_command, fractal_ssh=self.fractal_ssh)
1265
- logger.warning(f"Unlink {tarfile_path_remote=} - START")
1266
- self.fractal_ssh.sftp().unlink(tarfile_path_remote)
1267
- logger.warning(f"Unlink {tarfile_path_remote=} - STOP")
1258
+ # Remove remote tarfile
1259
+ rm_command = f"rm {tarfile_path_remote}"
1260
+ self.fractal_ssh.run_command(cmd=rm_command)
1268
1261
 
1269
1262
  # Create remote tarfile
1270
1263
  tar_command = (
@@ -1278,7 +1271,7 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1278
1271
 
1279
1272
  # Fetch tarfile
1280
1273
  t_0_get = time.perf_counter()
1281
- self.fractal_ssh.get(
1274
+ self.fractal_ssh.fetch_file(
1282
1275
  remote=tarfile_path_remote,
1283
1276
  local=tarfile_path_local,
1284
1277
  )
@@ -1291,6 +1284,11 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1291
1284
  # Extract tarfile locally
1292
1285
  extract_archive(Path(tarfile_path_local))
1293
1286
 
1287
+ # Remove local tarfile
1288
+ if Path(tarfile_path_local).exists():
1289
+ logger.warning(f"Remove existing file {tarfile_path_local}.")
1290
+ Path(tarfile_path_local).unlink()
1291
+
1294
1292
  t_1 = time.perf_counter()
1295
1293
  logger.info("[_get_subfolder_sftp] End - " f"elapsed: {t_1-t_0:.3f} s")
1296
1294
 
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import logging
2
3
  import time
3
4
  from contextlib import contextmanager
@@ -23,21 +24,58 @@ class FractalSSHTimeoutError(RuntimeError):
23
24
  pass
24
25
 
25
26
 
26
- class FractalSSHListTimeoutError(RuntimeError):
27
- pass
27
+ logger = set_logger(__name__)
28
28
 
29
29
 
30
- logger = set_logger(__name__)
30
+ @contextmanager
31
+ def _acquire_lock_with_timeout(
32
+ lock: Lock,
33
+ label: str,
34
+ timeout: float,
35
+ logger_name: str = __name__,
36
+ ) -> Generator[Literal[True], Any, None]:
37
+ """
38
+ Given a `threading.Lock` object, try to acquire it within a given timeout.
39
+
40
+ Arguments:
41
+ lock:
42
+ label:
43
+ timeout:
44
+ logger_name:
45
+ """
46
+ logger = get_logger(logger_name)
47
+ logger.info(f"Trying to acquire lock for '{label}', with {timeout=}")
48
+ result = lock.acquire(timeout=timeout)
49
+ try:
50
+ if not result:
51
+ logger.error(f"Lock for '{label}' was *not* acquired.")
52
+ raise FractalSSHTimeoutError(
53
+ f"Failed to acquire lock for '{label}' within "
54
+ f"{timeout} seconds"
55
+ )
56
+ logger.info(f"Lock for '{label}' was acquired.")
57
+ yield result
58
+ finally:
59
+ if result:
60
+ lock.release()
61
+ logger.info(f"Lock for '{label}' was released.")
31
62
 
32
63
 
33
64
  class FractalSSH(object):
34
65
  """
35
- FIXME SSH: Fix docstring
66
+ Wrapper of `fabric.Connection` object, enriched with locks.
67
+
68
+ Note: methods marked as `_unsafe` should not be used directly,
69
+ since they do not enforce locking.
36
70
 
37
71
  Attributes:
38
72
  _lock:
39
- connection:
73
+ _connection:
40
74
  default_lock_timeout:
75
+ default_max_attempts:
76
+ default_base_interval:
77
+ sftp_get_prefetch:
78
+ sftp_get_max_requests:
41
79
  logger_name:
42
80
  """
43
81
 
@@ -46,6 +84,8 @@ class FractalSSH(object):
46
84
  default_lock_timeout: float
47
85
  default_max_attempts: int
48
86
  default_base_interval: float
87
+ sftp_get_prefetch: bool
88
+ sftp_get_max_requests: int
49
89
  logger_name: str
50
90
 
51
91
  def __init__(
@@ -54,6 +94,8 @@ class FractalSSH(object):
54
94
  default_timeout: float = 250,
55
95
  default_max_attempts: int = 5,
56
96
  default_base_interval: float = 3.0,
97
+ sftp_get_prefetch: bool = False,
98
+ sftp_get_max_requests: int = 64,
57
99
  logger_name: str = __name__,
58
100
  ):
59
101
  self._lock = Lock()
@@ -61,28 +103,11 @@ class FractalSSH(object):
61
103
  self.default_lock_timeout = default_timeout
62
104
  self.default_base_interval = default_base_interval
63
105
  self.default_max_attempts = default_max_attempts
106
+ self.sftp_get_prefetch = sftp_get_prefetch
107
+ self.sftp_get_max_requests = sftp_get_max_requests
64
108
  self.logger_name = logger_name
65
109
  set_logger(self.logger_name)
66
110
 
67
- @contextmanager
68
- def acquire_timeout(
69
- self, timeout: float
70
- ) -> Generator[Literal[True], Any, None]:
71
- self.logger.debug(f"Trying to acquire lock, with {timeout=}")
72
- result = self._lock.acquire(timeout=timeout)
73
- try:
74
- if not result:
75
- self.logger.error("Lock was *NOT* acquired.")
76
- raise FractalSSHTimeoutError(
77
- f"Failed to acquire lock within {timeout} seconds"
78
- )
79
- self.logger.debug("Lock was acquired.")
80
- yield result
81
- finally:
82
- if result:
83
- self._lock.release()
84
- self.logger.debug("Lock was released")
85
-
86
111
  @property
87
112
  def is_connected(self) -> bool:
88
113
  return self._connection.is_connected
@@ -91,36 +116,82 @@ class FractalSSH(object):
91
116
  def logger(self) -> logging.Logger:
92
117
  return get_logger(self.logger_name)
93
118
 
94
- def put(
95
- self, *args, lock_timeout: Optional[float] = None, **kwargs
119
+ def _put(
120
+ self,
121
+ *,
122
+ local: str,
123
+ remote: str,
124
+ label: str,
125
+ lock_timeout: Optional[float] = None,
96
126
  ) -> Result:
127
+ """
128
+ Transfer a local file to a remote path, via SFTP.
129
+ """
97
130
  actual_lock_timeout = self.default_lock_timeout
98
131
  if lock_timeout is not None:
99
132
  actual_lock_timeout = lock_timeout
100
- with self.acquire_timeout(timeout=actual_lock_timeout):
101
- return self._connection.put(*args, **kwargs)
133
+ with _acquire_lock_with_timeout(
134
+ lock=self._lock,
135
+ label=label,
136
+ timeout=actual_lock_timeout,
137
+ ):
138
+ return self._sftp_unsafe().put(local, remote)
102
139
 
103
- def get(
104
- self, *args, lock_timeout: Optional[float] = None, **kwargs
140
+ def _get(
141
+ self,
142
+ *,
143
+ local: str,
144
+ remote: str,
145
+ label: str,
146
+ lock_timeout: Optional[float] = None,
105
147
  ) -> Result:
106
148
  actual_lock_timeout = self.default_lock_timeout
107
149
  if lock_timeout is not None:
108
150
  actual_lock_timeout = lock_timeout
109
- with self.acquire_timeout(timeout=actual_lock_timeout):
110
- return self._connection.get(*args, **kwargs)
151
+ with _acquire_lock_with_timeout(
152
+ lock=self._lock,
153
+ label=label,
154
+ timeout=actual_lock_timeout,
155
+ ):
156
+ return self._sftp_unsafe().get(
157
+ remote,
158
+ local,
159
+ prefetch=self.sftp_get_prefetch,
160
+ max_concurrent_prefetch_requests=self.sftp_get_max_requests,
161
+ )
111
162
 
112
- def run(
113
- self, *args, lock_timeout: Optional[float] = None, **kwargs
163
+ def _run(
164
+ self, *args, label: str, lock_timeout: Optional[float] = None, **kwargs
114
165
  ) -> Any:
115
166
  actual_lock_timeout = self.default_lock_timeout
116
167
  if lock_timeout is not None:
117
168
  actual_lock_timeout = lock_timeout
118
- with self.acquire_timeout(timeout=actual_lock_timeout):
169
+ with _acquire_lock_with_timeout(
170
+ lock=self._lock,
171
+ label=label,
172
+ timeout=actual_lock_timeout,
173
+ ):
119
174
  return self._connection.run(*args, **kwargs)
120
175
 
121
- def sftp(self) -> paramiko.sftp_client.SFTPClient:
176
+ def _sftp_unsafe(self) -> paramiko.sftp_client.SFTPClient:
177
+ """
178
+ This is marked as unsafe because you should only use its methods
179
+ after acquiring a lock.
180
+ """
122
181
  return self._connection.sftp()
123
182
 
183
+ def read_remote_json_file(self, filepath: str) -> dict[str, Any]:
184
+ self.logger.info(f"START reading remote JSON file {filepath}.")
185
+ with _acquire_lock_with_timeout(
186
+ lock=self._lock,
187
+ label="read_remote_json_file",
188
+ timeout=self.default_lock_timeout,
189
+ ):
190
+ with self._sftp_unsafe().open(filepath, "r") as f:
191
+ data = json.load(f)
192
+ self.logger.info(f"END reading remote JSON file {filepath}.")
193
+ return data
194
+
124
195
  def check_connection(self) -> None:
125
196
  """
126
197
  Open the SSH connection and handle exceptions.
@@ -131,7 +202,12 @@ class FractalSSH(object):
131
202
  """
132
203
  if not self._connection.is_connected:
133
204
  try:
134
- self._connection.open()
205
+ with _acquire_lock_with_timeout(
206
+ lock=self._lock,
207
+ label="_connection.open",
208
+ timeout=self.default_lock_timeout,
209
+ ):
210
+ self._connection.open()
135
211
  except Exception as e:
136
212
  raise RuntimeError(
137
213
  f"Cannot open SSH connection. Original error:\n{str(e)}"
@@ -146,8 +222,12 @@ class FractalSSH(object):
146
222
  because we observed cases where `is_connected=False` but the underlying
147
223
  `Transport` object was not closed.
148
224
  """
149
-
150
- self._connection.close()
225
+ with _acquire_lock_with_timeout(
226
+ lock=self._lock,
227
+ label="_connection.close",
228
+ timeout=self.default_lock_timeout,
229
+ ):
230
+ self._connection.close()
151
231
 
152
232
  if self._connection.client is not None:
153
233
  self._connection.client.close()
@@ -197,8 +277,11 @@ class FractalSSH(object):
197
277
  self.logger.info(f"{prefix} START running '{cmd}' over SSH.")
198
278
  try:
199
279
  # Case 1: Command runs successfully
200
- res = self.run(
201
- cmd, lock_timeout=actual_lock_timeout, hide=True
280
+ res = self._run(
281
+ cmd,
282
+ label=f"run {cmd}",
283
+ lock_timeout=actual_lock_timeout,
284
+ hide=True,
202
285
  )
203
286
  t_1 = time.perf_counter()
204
287
  self.logger.info(
@@ -250,7 +333,6 @@ class FractalSSH(object):
250
333
  *,
251
334
  local: str,
252
335
  remote: str,
253
- logger_name: Optional[str] = None,
254
336
  lock_timeout: Optional[float] = None,
255
337
  ) -> None:
256
338
  """
@@ -261,28 +343,65 @@ class FractalSSH(object):
261
343
  remote: Target path on remote host
262
344
  fractal_ssh: FractalSSH connection object with custom lock
263
345
  logger_name: Name of the logger
264
-
265
346
  """
266
347
  try:
267
- self.put(local=local, remote=remote, lock_timeout=lock_timeout)
348
+ prefix = "[send_file]"
349
+ self.logger.info(f"{prefix} START transfer of '{local}' over SSH.")
350
+ self._put(
351
+ local=local,
352
+ remote=remote,
353
+ lock_timeout=lock_timeout,
354
+ label=f"send_file {local=} {remote=}",
355
+ )
356
+ self.logger.info(f"{prefix} END transfer of '{local}' over SSH.")
268
357
  except Exception as e:
269
- logger = get_logger(logger_name=logger_name)
270
- logger.error(
358
+ self.logger.error(
271
359
  f"Transferring {local=} to {remote=} over SSH failed.\n"
272
360
  f"Original Error:\n{str(e)}."
273
361
  )
274
362
  raise e
275
363
 
364
+ def fetch_file(
365
+ self,
366
+ *,
367
+ local: str,
368
+ remote: str,
369
+ lock_timeout: Optional[float] = None,
370
+ ) -> None:
371
+ """
372
+ Transfer a file via SSH
373
+
374
+ Args:
375
+ local: Local path to file
376
+ remote: Target path on remote host
377
+ logger_name: Name of the logger
378
+ lock_timeout:
379
+ """
380
+ try:
381
+ prefix = "[fetch_file] "
382
+ self.logger.info(f"{prefix} START fetching '{remote}' over SSH.")
383
+ self._get(
384
+ local=local,
385
+ remote=remote,
386
+ lock_timeout=lock_timeout,
387
+ label=f"fetch_file {local=} {remote=}",
388
+ )
389
+ self.logger.info(f"{prefix} END fetching '{remote}' over SSH.")
390
+ except Exception as e:
391
+ self.logger.error(
392
+ f"Transferring {remote=} to {local=} over SSH failed.\n"
393
+ f"Original Error:\n{str(e)}."
394
+ )
395
+ raise e
396
+
276
397
  def mkdir(self, *, folder: str, parents: bool = True) -> None:
277
398
  """
278
399
  Create a folder remotely via SSH.
279
400
 
280
401
  Args:
281
402
  folder:
282
- fractal_ssh:
283
403
  parents:
284
404
  """
285
- # FIXME SSH: try using `mkdir` method of `paramiko.SFTPClient`
286
405
  if parents:
287
406
  cmd = f"mkdir -p {folder}"
288
407
  else:
@@ -339,12 +458,18 @@ class FractalSSH(object):
339
458
  contents: File contents
340
459
  lock_timeout:
341
460
  """
461
+ self.logger.info(f"START writing to remote file {path}.")
342
462
  actual_lock_timeout = self.default_lock_timeout
343
463
  if lock_timeout is not None:
344
464
  actual_lock_timeout = lock_timeout
345
- with self.acquire_timeout(timeout=actual_lock_timeout):
346
- with self.sftp().open(filename=path, mode="w") as f:
465
+ with _acquire_lock_with_timeout(
466
+ lock=self._lock,
467
+ label=f"write_remote_file {path=}",
468
+ timeout=actual_lock_timeout,
469
+ ):
470
+ with self._sftp_unsafe().open(filename=path, mode="w") as f:
347
471
  f.write(content)
472
+ self.logger.info(f"END writing to remote file {path}.")
348
473
 
349
474
 
350
475
  class FractalSSHList(object):
@@ -425,7 +550,11 @@ class FractalSSHList(object):
425
550
  "look_for_keys": False,
426
551
  },
427
552
  )
428
- with self.acquire_lock_with_timeout():
553
+ with _acquire_lock_with_timeout(
554
+ lock=self._lock,
555
+ label="FractalSSHList.get",
556
+ timeout=self._timeout,
557
+ ):
429
558
  self._data[key] = FractalSSH(connection=connection)
430
559
  return self._data[key]
431
560
 
@@ -465,7 +594,11 @@ class FractalSSHList(object):
465
594
  key_path:
466
595
  """
467
596
  key = (host, user, key_path)
468
- with self.acquire_lock_with_timeout():
597
+ with _acquire_lock_with_timeout(
598
+ lock=self._lock,
599
+ timeout=self._timeout,
600
+ label="FractalSSHList.remove",
601
+ ):
469
602
  self.logger.info(
470
603
  f"Removing FractalSSH object for {user}@{host} "
471
604
  "from collection."
@@ -492,24 +625,4 @@ class FractalSSHList(object):
492
625
  f"Closing FractalSSH object for {user}@{host} "
493
626
  f"({fractal_ssh_obj.is_connected=})."
494
627
  )
495
- with fractal_ssh_obj.acquire_timeout(timeout=timeout):
496
- fractal_ssh_obj.close()
497
-
498
- @contextmanager
499
- def acquire_lock_with_timeout(self) -> Generator[Literal[True], Any, None]:
500
- self.logger.debug(
501
- f"Trying to acquire lock, with timeout {self._timeout} s"
502
- )
503
- result = self._lock.acquire(timeout=self._timeout)
504
- try:
505
- if not result:
506
- self.logger.error("Lock was *NOT* acquired.")
507
- raise FractalSSHListTimeoutError(
508
- f"Failed to acquire lock within {self._timeout} ss"
509
- )
510
- self.logger.debug("Lock was acquired.")
511
- yield result
512
- finally:
513
- if result:
514
- self._lock.release()
515
- self.logger.debug("Lock was released")
628
+ fractal_ssh_obj.close()
@@ -1,4 +1,3 @@
1
- import json
2
1
  import os
3
2
  from pathlib import Path
4
3
  from tempfile import TemporaryDirectory
@@ -144,6 +143,13 @@ def background_collect_pip_ssh(
144
143
  for key, value in task_group.model_dump().items():
145
144
  logger.debug(f"task_group.{key}: {value}")
146
145
 
146
+ # `remove_venv_folder_upon_failure` is set to True only if
147
+ # script 1 goes through, which means that the remote folder
148
+ # `package_env_dir` did not already exist. If this remote
149
+ # folder already existed, then script 1 fails and the boolean
150
+ # flag `remove_venv_folder_upon_failure` remains false.
151
+ remove_venv_folder_upon_failure = False
152
+
147
153
  # Open a DB session soon, since it is needed for updating `state`
148
154
  with next(get_sync_db()) as db:
149
155
  try:
@@ -187,12 +193,11 @@ def background_collect_pip_ssh(
187
193
  # long operations that do not use the db
188
194
  db.close()
189
195
 
190
- # `remove_venv_folder_upon_failure` is set to True only if
191
- # script 1 goes through, which means that the remote folder
192
- # `package_env_dir` did not already exist. If this remote
193
- # folder already existed, then script 1 fails and the boolean
194
- # flag `remove_venv_folder_upon_failure` remains false.
195
- remove_venv_folder_upon_failure = False
196
+ # Create remote folder (note that because of `parents=True` we
197
+ # are in the `no error if existing, make parent directories as
198
+ # needed` scenario)
199
+ fractal_ssh.mkdir(folder=tasks_base_dir, parents=True)
200
+
196
201
  stdout = _customize_and_run_template(
197
202
  script_filename="_1_create_venv.sh",
198
203
  **common_args,
@@ -263,8 +268,9 @@ def background_collect_pip_ssh(
263
268
  ).as_posix()
264
269
 
265
270
  # Read and validate remote manifest file
266
- with fractal_ssh.sftp().open(manifest_path_remote, "r") as f:
267
- pkg_manifest_dict = json.load(f)
271
+ pkg_manifest_dict = fractal_ssh.read_remote_json_file(
272
+ manifest_path_remote
273
+ )
268
274
  logger.info(f"collecting - loaded {manifest_path_remote=}")
269
275
  pkg_manifest = ManifestV2(**pkg_manifest_dict)
270
276
  logger.info("collecting - manifest is a valid ManifestV2")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fractal-server"
3
- version = "2.7.0a8"
3
+ version = "2.7.0a10"
4
4
  description = "Server component of the Fractal analytics platform"
5
5
  authors = [
6
6
  "Tommaso Comparin <tommaso.comparin@exact-lab.it>",
@@ -92,7 +92,7 @@ filterwarnings = [
92
92
  ]
93
93
 
94
94
  [tool.bumpver]
95
- current_version = "2.7.0a8"
95
+ current_version = "2.7.0a10"
96
96
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
97
97
  commit_message = "bump version {old_version} -> {new_version}"
98
98
  commit = true
@@ -1 +0,0 @@
1
- __VERSION__ = "2.7.0a8"