fractal-server 2.16.5__py3-none-any.whl → 2.17.0a0__py3-none-any.whl

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 (113) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +129 -22
  3. fractal_server/app/db/__init__.py +9 -11
  4. fractal_server/app/models/security.py +7 -3
  5. fractal_server/app/models/user_settings.py +0 -4
  6. fractal_server/app/models/v2/__init__.py +4 -0
  7. fractal_server/app/models/v2/job.py +3 -4
  8. fractal_server/app/models/v2/profile.py +16 -0
  9. fractal_server/app/models/v2/project.py +3 -0
  10. fractal_server/app/models/v2/resource.py +130 -0
  11. fractal_server/app/models/v2/task_group.py +3 -0
  12. fractal_server/app/routes/admin/v2/__init__.py +4 -0
  13. fractal_server/app/routes/admin/v2/_aux_functions.py +55 -0
  14. fractal_server/app/routes/admin/v2/profile.py +86 -0
  15. fractal_server/app/routes/admin/v2/resource.py +229 -0
  16. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +48 -82
  17. fractal_server/app/routes/api/__init__.py +26 -7
  18. fractal_server/app/routes/api/v2/_aux_functions.py +27 -1
  19. fractal_server/app/routes/api/v2/_aux_functions_history.py +2 -2
  20. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
  21. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +7 -7
  22. fractal_server/app/routes/api/v2/project.py +5 -1
  23. fractal_server/app/routes/api/v2/submit.py +32 -24
  24. fractal_server/app/routes/api/v2/task.py +5 -0
  25. fractal_server/app/routes/api/v2/task_collection.py +36 -47
  26. fractal_server/app/routes/api/v2/task_collection_custom.py +11 -5
  27. fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -40
  28. fractal_server/app/routes/api/v2/task_group_lifecycle.py +39 -82
  29. fractal_server/app/routes/api/v2/workflow_import.py +4 -3
  30. fractal_server/app/routes/auth/_aux_auth.py +3 -3
  31. fractal_server/app/routes/auth/current_user.py +45 -7
  32. fractal_server/app/routes/auth/oauth.py +1 -1
  33. fractal_server/app/routes/auth/users.py +9 -0
  34. fractal_server/app/routes/aux/_runner.py +2 -1
  35. fractal_server/app/routes/aux/validate_user_profile.py +62 -0
  36. fractal_server/app/routes/aux/validate_user_settings.py +12 -9
  37. fractal_server/app/schemas/user.py +20 -13
  38. fractal_server/app/schemas/user_settings.py +0 -4
  39. fractal_server/app/schemas/v2/__init__.py +11 -0
  40. fractal_server/app/schemas/v2/profile.py +72 -0
  41. fractal_server/app/schemas/v2/resource.py +117 -0
  42. fractal_server/app/security/__init__.py +6 -13
  43. fractal_server/app/security/signup_email.py +2 -2
  44. fractal_server/app/user_settings.py +2 -12
  45. fractal_server/config/__init__.py +23 -0
  46. fractal_server/config/_database.py +58 -0
  47. fractal_server/config/_email.py +170 -0
  48. fractal_server/config/_init_data.py +27 -0
  49. fractal_server/config/_main.py +216 -0
  50. fractal_server/config/_settings_config.py +7 -0
  51. fractal_server/images/tools.py +3 -3
  52. fractal_server/logger.py +3 -3
  53. fractal_server/main.py +14 -21
  54. fractal_server/migrations/versions/90f6508c6379_drop_useroauth_username.py +36 -0
  55. fractal_server/migrations/versions/a80ac5a352bf_resource_profile.py +195 -0
  56. fractal_server/runner/config/__init__.py +2 -0
  57. fractal_server/runner/config/_local.py +21 -0
  58. fractal_server/runner/config/_slurm.py +128 -0
  59. fractal_server/runner/config/slurm_mem_to_MB.py +63 -0
  60. fractal_server/runner/exceptions.py +4 -0
  61. fractal_server/runner/executors/base_runner.py +17 -7
  62. fractal_server/runner/executors/local/get_local_config.py +21 -86
  63. fractal_server/runner/executors/local/runner.py +48 -5
  64. fractal_server/runner/executors/slurm_common/_batching.py +2 -2
  65. fractal_server/runner/executors/slurm_common/base_slurm_runner.py +59 -25
  66. fractal_server/runner/executors/slurm_common/get_slurm_config.py +38 -54
  67. fractal_server/runner/executors/slurm_common/remote.py +1 -1
  68. fractal_server/runner/executors/slurm_common/{_slurm_config.py → slurm_config.py} +3 -254
  69. fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -1
  70. fractal_server/runner/executors/slurm_ssh/runner.py +12 -14
  71. fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +2 -2
  72. fractal_server/runner/executors/slurm_sudo/runner.py +12 -12
  73. fractal_server/runner/v2/_local.py +36 -21
  74. fractal_server/runner/v2/_slurm_ssh.py +40 -4
  75. fractal_server/runner/v2/_slurm_sudo.py +41 -11
  76. fractal_server/runner/v2/db_tools.py +1 -1
  77. fractal_server/runner/v2/runner.py +3 -11
  78. fractal_server/runner/v2/runner_functions.py +42 -28
  79. fractal_server/runner/v2/submit_workflow.py +87 -108
  80. fractal_server/runner/versions.py +8 -3
  81. fractal_server/ssh/_fabric.py +6 -6
  82. fractal_server/tasks/config/__init__.py +3 -0
  83. fractal_server/tasks/config/_pixi.py +127 -0
  84. fractal_server/tasks/config/_python.py +51 -0
  85. fractal_server/tasks/v2/local/_utils.py +7 -7
  86. fractal_server/tasks/v2/local/collect.py +13 -5
  87. fractal_server/tasks/v2/local/collect_pixi.py +26 -10
  88. fractal_server/tasks/v2/local/deactivate.py +7 -1
  89. fractal_server/tasks/v2/local/deactivate_pixi.py +5 -1
  90. fractal_server/tasks/v2/local/delete.py +4 -0
  91. fractal_server/tasks/v2/local/reactivate.py +13 -5
  92. fractal_server/tasks/v2/local/reactivate_pixi.py +27 -9
  93. fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py +11 -10
  94. fractal_server/tasks/v2/ssh/_utils.py +6 -7
  95. fractal_server/tasks/v2/ssh/collect.py +19 -12
  96. fractal_server/tasks/v2/ssh/collect_pixi.py +34 -16
  97. fractal_server/tasks/v2/ssh/deactivate.py +12 -8
  98. fractal_server/tasks/v2/ssh/deactivate_pixi.py +14 -10
  99. fractal_server/tasks/v2/ssh/delete.py +12 -9
  100. fractal_server/tasks/v2/ssh/reactivate.py +18 -12
  101. fractal_server/tasks/v2/ssh/reactivate_pixi.py +36 -17
  102. fractal_server/tasks/v2/templates/4_pip_show.sh +4 -6
  103. fractal_server/tasks/v2/utils_database.py +2 -2
  104. fractal_server/tasks/v2/utils_python_interpreter.py +8 -16
  105. fractal_server/tasks/v2/utils_templates.py +7 -10
  106. fractal_server/utils.py +1 -1
  107. {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/METADATA +5 -5
  108. {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/RECORD +112 -90
  109. {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/WHEEL +1 -1
  110. fractal_server/config.py +0 -906
  111. /fractal_server/{runner → app}/shutdown.py +0 -0
  112. {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/entry_points.txt +0 -0
  113. {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info/licenses}/LICENSE +0 -0
@@ -112,7 +112,7 @@ class SlurmJob(BaseModel):
112
112
  return self.slurm_stderr_remote_path.as_posix()
113
113
 
114
114
  @property
115
- def slurm_stdout_local_path(self) -> str:
115
+ def slurm_stdout_local_path(self) -> Path:
116
116
  return (
117
117
  self.workdir_local
118
118
  / f"{self.prefix}-slurm-{self.slurm_job_id_placeholder}.out"
@@ -6,13 +6,13 @@ from ..slurm_common.slurm_job_task_models import SlurmJob
6
6
  from .run_subprocess import run_subprocess
7
7
  from .tar_commands import get_tar_compression_cmd
8
8
  from .tar_commands import get_tar_extraction_cmd
9
- from fractal_server.config import get_settings
9
+ from fractal_server.app.models import Profile
10
+ from fractal_server.app.models import Resource
10
11
  from fractal_server.logger import set_logger
12
+ from fractal_server.runner.config import JobRunnerConfigSLURM
11
13
  from fractal_server.ssh._fabric import FractalSSH
12
14
  from fractal_server.ssh._fabric import FractalSSHCommandError
13
15
  from fractal_server.ssh._fabric import FractalSSHTimeoutError
14
- from fractal_server.syringe import Inject
15
-
16
16
 
17
17
  logger = set_logger(__name__)
18
18
 
@@ -27,10 +27,11 @@ class SlurmSSHRunner(BaseSlurmRunner):
27
27
  root_dir_local: Path,
28
28
  root_dir_remote: Path,
29
29
  common_script_lines: list[str] | None = None,
30
- user_cache_dir: str | None = None,
31
- poll_interval: int | None = None,
30
+ resource: Resource,
32
31
  # Specific
33
32
  slurm_account: str | None = None,
33
+ profile: Profile,
34
+ user_cache_dir: str | None = None,
34
35
  fractal_ssh: FractalSSH,
35
36
  ) -> None:
36
37
  """
@@ -38,20 +39,21 @@ class SlurmSSHRunner(BaseSlurmRunner):
38
39
  different SLURM jobs/tasks.
39
40
  """
40
41
  self.fractal_ssh = fractal_ssh
42
+ self.shared_config = JobRunnerConfigSLURM(
43
+ **resource.jobs_runner_config
44
+ )
41
45
  logger.warning(self.fractal_ssh)
42
46
 
43
47
  # Check SSH connection and try to recover from a closed-socket error
44
48
  self.fractal_ssh.check_connection()
45
-
46
- settings = Inject(get_settings)
47
49
  super().__init__(
48
50
  slurm_runner_type="ssh",
49
51
  root_dir_local=root_dir_local,
50
52
  root_dir_remote=root_dir_remote,
51
53
  common_script_lines=common_script_lines,
52
54
  user_cache_dir=user_cache_dir,
53
- poll_interval=poll_interval,
54
- python_worker_interpreter=settings.FRACTAL_SLURM_WORKER_PYTHON,
55
+ poll_interval=resource.jobs_poll_interval,
56
+ python_worker_interpreter=resource.jobs_slurm_python_worker,
55
57
  slurm_account=slurm_account,
56
58
  )
57
59
 
@@ -225,11 +227,7 @@ class SlurmSSHRunner(BaseSlurmRunner):
225
227
 
226
228
  logger.debug("[_send_many_job_inputs] END.")
227
229
 
228
- def run_squeue(
229
- self,
230
- *,
231
- job_ids: list[str],
232
- ) -> str:
230
+ def run_squeue(self, *, job_ids: list[str]) -> str:
233
231
  """
234
232
  Run `squeue` for a set of SLURM job IDs.
235
233
 
@@ -33,7 +33,7 @@ def _run_command_as_user(
33
33
  """
34
34
  Use `sudo -u` to impersonate another user and run a command
35
35
 
36
- Arguments:
36
+ Args:
37
37
  cmd: Command to be run
38
38
  user: User to be impersonated
39
39
  check: If `True`, check that `returncode=0` and fail otherwise.
@@ -71,7 +71,7 @@ def _mkdir_as_user(*, folder: str, user: str) -> None:
71
71
  """
72
72
  Create a folder as a different user
73
73
 
74
- Arguments:
74
+ Args:
75
75
  folder: Absolute path to the folder
76
76
  user: User to be impersonated
77
77
 
@@ -2,7 +2,6 @@ import logging
2
2
  import os
3
3
  import shlex
4
4
  import subprocess # nosec
5
- import sys
6
5
  from concurrent.futures import ThreadPoolExecutor
7
6
  from pathlib import Path
8
7
 
@@ -10,10 +9,11 @@ from ..slurm_common.base_slurm_runner import BaseSlurmRunner
10
9
  from ..slurm_common.slurm_job_task_models import SlurmJob
11
10
  from ._subprocess_run_as_user import _mkdir_as_user
12
11
  from ._subprocess_run_as_user import _run_command_as_user
13
- from fractal_server.config import get_settings
12
+ from fractal_server.app.models import Profile
13
+ from fractal_server.app.models import Resource
14
14
  from fractal_server.logger import set_logger
15
+ from fractal_server.runner.config import JobRunnerConfigSLURM
15
16
  from fractal_server.runner.exceptions import JobExecutionError
16
- from fractal_server.syringe import Inject
17
17
 
18
18
  logger = set_logger(__name__)
19
19
 
@@ -51,19 +51,21 @@ class SudoSlurmRunner(BaseSlurmRunner):
51
51
  root_dir_local: Path,
52
52
  root_dir_remote: Path,
53
53
  common_script_lines: list[str] | None = None,
54
- user_cache_dir: str | None = None,
55
- poll_interval: int | None = None,
54
+ resource: Resource,
56
55
  # Specific
56
+ profile: Profile,
57
+ user_cache_dir: str | None = None, # FIXME: make required?
57
58
  slurm_account: str | None = None,
58
- slurm_user: str,
59
59
  ) -> None:
60
60
  """
61
61
  Set parameters that are the same for different Fractal tasks and for
62
62
  different SLURM jobs/tasks.
63
63
  """
64
64
 
65
- self.slurm_user = slurm_user
66
- settings = Inject(get_settings)
65
+ self.slurm_user = profile.username
66
+ self.shared_config = JobRunnerConfigSLURM(
67
+ **resource.jobs_runner_config
68
+ )
67
69
 
68
70
  super().__init__(
69
71
  slurm_runner_type="sudo",
@@ -71,10 +73,8 @@ class SudoSlurmRunner(BaseSlurmRunner):
71
73
  root_dir_remote=root_dir_remote,
72
74
  common_script_lines=common_script_lines,
73
75
  user_cache_dir=user_cache_dir,
74
- poll_interval=poll_interval,
75
- python_worker_interpreter=(
76
- settings.FRACTAL_SLURM_WORKER_PYTHON or sys.executable
77
- ),
76
+ poll_interval=resource.jobs_poll_interval,
77
+ python_worker_interpreter=resource.jobs_slurm_python_worker,
78
78
  slurm_account=slurm_account,
79
79
  )
80
80
 
@@ -5,16 +5,19 @@ from ..executors.local.runner import LocalRunner
5
5
  from ..set_start_and_last_task_index import set_start_and_last_task_index
6
6
  from .runner import execute_tasks_v2
7
7
  from fractal_server.app.models.v2 import DatasetV2
8
+ from fractal_server.app.models.v2 import Profile
9
+ from fractal_server.app.models.v2 import Resource
8
10
  from fractal_server.app.models.v2 import WorkflowV2
11
+ from fractal_server.ssh._fabric import FractalSSH
9
12
  from fractal_server.types import AttributeFilters
10
13
 
11
14
 
12
15
  def process_workflow(
13
16
  *,
17
+ job_id: int,
14
18
  workflow: WorkflowV2,
15
19
  dataset: DatasetV2,
16
20
  workflow_dir_local: Path,
17
- job_id: int,
18
21
  workflow_dir_remote: Path | None = None,
19
22
  first_task_index: int | None = None,
20
23
  last_task_index: int | None = None,
@@ -22,23 +25,24 @@ def process_workflow(
22
25
  job_attribute_filters: AttributeFilters,
23
26
  job_type_filters: dict[str, bool],
24
27
  user_id: int,
25
- **kwargs,
28
+ resource: Resource,
29
+ profile: Profile,
30
+ user_cache_dir: str | None = None,
31
+ fractal_ssh: FractalSSH | None = None,
32
+ slurm_account: str | None = None,
33
+ worker_init: str | None = None,
26
34
  ) -> None:
27
35
  """
28
- Run a workflow through
36
+ Run a workflow through a local backend.
29
37
 
30
38
  Args:
31
- workflow:
32
- The workflow to be run
33
- dataset:
34
- Initial dataset.
35
- workflow_dir_local:
36
- Working directory for this run.
39
+ job_id: Job ID.
40
+ workflow: Workflow to be run
41
+ dataset: Dataset to be used.
42
+ workflow_dir_local: Local working directory for this job.
37
43
  workflow_dir_remote:
38
- Working directory for this run, on the user side. This argument is
39
- present for compatibility with the standard backend interface, but
40
- for the `local` backend it cannot be different from
41
- `workflow_dir_local`.
44
+ Remote working directory for this job - only relevant for
45
+ `slurm_sudo` and `slurm_ssh` backends.
42
46
  first_task_index:
43
47
  Positional index of the first task to execute; if `None`, start
44
48
  from `0`.
@@ -46,13 +50,20 @@ def process_workflow(
46
50
  Positional index of the last task to execute; if `None`, proceed
47
51
  until the last task.
48
52
  logger_name: Logger name
49
- user_id:
50
-
51
- Raises:
52
- TaskExecutionError: wrapper for errors raised during tasks' execution
53
- (positive exit codes).
54
- JobExecutionError: wrapper for errors raised by the tasks' executors
55
- (negative exit codes).
53
+ user_id: User ID.
54
+ resource: Computational resource for running this job.
55
+ profile: Computational profile for running this job.
56
+ user_cache_dir:
57
+ User-writeable folder (typically a subfolder of `project_dir`).
58
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
59
+ fractal_ssh:
60
+ `FractalSSH` object, only relevant for the `slurm_ssh` backend.
61
+ slurm_account:
62
+ SLURM account to set.
63
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
64
+ worker_init:
65
+ Additional preamble lines for SLURM submission script.
66
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
56
67
  """
57
68
 
58
69
  if workflow_dir_remote and (workflow_dir_remote != workflow_dir_local):
@@ -69,7 +80,11 @@ def process_workflow(
69
80
  last_task_index=last_task_index,
70
81
  )
71
82
 
72
- with LocalRunner(root_dir_local=workflow_dir_local) as runner:
83
+ with LocalRunner(
84
+ root_dir_local=workflow_dir_local,
85
+ resource=resource,
86
+ profile=profile,
87
+ ) as runner:
73
88
  execute_tasks_v2(
74
89
  wf_task_list=workflow.task_list[
75
90
  first_task_index : (last_task_index + 1)
@@ -23,6 +23,8 @@ from ..executors.slurm_ssh.runner import SlurmSSHRunner
23
23
  from ..set_start_and_last_task_index import set_start_and_last_task_index
24
24
  from .runner import execute_tasks_v2
25
25
  from fractal_server.app.models.v2 import DatasetV2
26
+ from fractal_server.app.models.v2 import Profile
27
+ from fractal_server.app.models.v2 import Resource
26
28
  from fractal_server.app.models.v2 import WorkflowV2
27
29
  from fractal_server.logger import set_logger
28
30
  from fractal_server.types import AttributeFilters
@@ -32,10 +34,10 @@ logger = set_logger(__name__)
32
34
 
33
35
  def process_workflow(
34
36
  *,
37
+ job_id: int,
35
38
  workflow: WorkflowV2,
36
39
  dataset: DatasetV2,
37
40
  workflow_dir_local: Path,
38
- job_id: int,
39
41
  workflow_dir_remote: Path | None = None,
40
42
  first_task_index: int | None = None,
41
43
  last_task_index: int | None = None,
@@ -43,13 +45,45 @@ def process_workflow(
43
45
  job_attribute_filters: AttributeFilters,
44
46
  job_type_filters: dict[str, bool],
45
47
  user_id: int,
46
- # SLURM-ssh-specific
47
- fractal_ssh: FractalSSH,
48
+ resource: Resource,
49
+ profile: Profile,
50
+ fractal_ssh: FractalSSH | None = None,
48
51
  slurm_account: str | None = None,
49
52
  worker_init: str | None = None,
53
+ user_cache_dir: str | None = None,
50
54
  ) -> None:
51
55
  """
52
- Process workflow (SLURM backend public interface)
56
+ Run a workflow through a `slurm_ssh` backend.
57
+
58
+ Args:
59
+ job_id: Job ID.
60
+ workflow: Workflow to be run
61
+ dataset: Dataset to be used.
62
+ workflow_dir_local: Local working directory for this job.
63
+ workflow_dir_remote:
64
+ Remote working directory for this job - only relevant for
65
+ `slurm_sudo` and `slurm_ssh` backends.
66
+ first_task_index:
67
+ Positional index of the first task to execute; if `None`, start
68
+ from `0`.
69
+ last_task_index:
70
+ Positional index of the last task to execute; if `None`, proceed
71
+ until the last task.
72
+ logger_name: Logger name
73
+ user_id: User ID.
74
+ resource: Computational resource for running this job.
75
+ profile: Computational profile for running this job.
76
+ user_cache_dir:
77
+ User-writeable folder (typically a subfolder of `project_dir`).
78
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
79
+ fractal_ssh:
80
+ `FractalSSH` object, only relevant for the `slurm_ssh` backend.
81
+ slurm_account:
82
+ SLURM account to set.
83
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
84
+ worker_init:
85
+ Additional preamble lines for SLURM submission script.
86
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
53
87
  """
54
88
 
55
89
  # Set values of first_task_index and last_task_index
@@ -68,6 +102,8 @@ def process_workflow(
68
102
  root_dir_local=workflow_dir_local,
69
103
  root_dir_remote=workflow_dir_remote,
70
104
  slurm_account=slurm_account,
105
+ resource=resource,
106
+ profile=profile,
71
107
  common_script_lines=worker_init,
72
108
  ) as runner:
73
109
  execute_tasks_v2(
@@ -22,16 +22,19 @@ from ..executors.slurm_sudo.runner import SudoSlurmRunner
22
22
  from ..set_start_and_last_task_index import set_start_and_last_task_index
23
23
  from .runner import execute_tasks_v2
24
24
  from fractal_server.app.models.v2 import DatasetV2
25
+ from fractal_server.app.models.v2 import Profile
26
+ from fractal_server.app.models.v2 import Resource
25
27
  from fractal_server.app.models.v2 import WorkflowV2
28
+ from fractal_server.ssh._fabric import FractalSSH
26
29
  from fractal_server.types import AttributeFilters
27
30
 
28
31
 
29
32
  def process_workflow(
30
33
  *,
34
+ job_id: int,
31
35
  workflow: WorkflowV2,
32
36
  dataset: DatasetV2,
33
37
  workflow_dir_local: Path,
34
- job_id: int,
35
38
  workflow_dir_remote: Path | None = None,
36
39
  first_task_index: int | None = None,
37
40
  last_task_index: int | None = None,
@@ -39,14 +42,45 @@ def process_workflow(
39
42
  job_attribute_filters: AttributeFilters,
40
43
  job_type_filters: dict[str, bool],
41
44
  user_id: int,
42
- # SLURM-sudo-specific
45
+ resource: Resource,
46
+ profile: Profile,
43
47
  user_cache_dir: str | None = None,
44
- slurm_user: str | None = None,
45
48
  slurm_account: str | None = None,
46
49
  worker_init: str | None = None,
50
+ fractal_ssh: FractalSSH | None = None,
47
51
  ) -> None:
48
52
  """
49
- Process workflow (SLURM backend public interface).
53
+ Run a workflow through a `slurm_sudo` backend.
54
+
55
+ Args:
56
+ job_id: Job ID.
57
+ workflow: Workflow to be run
58
+ dataset: Dataset to be used.
59
+ workflow_dir_local: Local working directory for this job.
60
+ workflow_dir_remote:
61
+ Remote working directory for this job - only relevant for
62
+ `slurm_sudo` and `slurm_ssh` backends.
63
+ first_task_index:
64
+ Positional index of the first task to execute; if `None`, start
65
+ from `0`.
66
+ last_task_index:
67
+ Positional index of the last task to execute; if `None`, proceed
68
+ until the last task.
69
+ logger_name: Logger name
70
+ user_id: User ID.
71
+ resource: Computational resource for running this job.
72
+ profile: Computational profile for running this job.
73
+ user_cache_dir:
74
+ User-writeable folder (typically a subfolder of `project_dir`).
75
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
76
+ fractal_ssh:
77
+ `FractalSSH` object, only relevant for the `slurm_ssh` backend.
78
+ slurm_account:
79
+ SLURM account to set.
80
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
81
+ worker_init:
82
+ Additional preamble lines for SLURM submission script.
83
+ Only relevant for `slurm_sudo` and `slurm_ssh` backends.
50
84
  """
51
85
 
52
86
  # Set values of first_task_index and last_task_index
@@ -57,20 +91,16 @@ def process_workflow(
57
91
  last_task_index=last_task_index,
58
92
  )
59
93
 
60
- if not slurm_user:
61
- raise RuntimeError(
62
- "slurm_user argument is required, for slurm backend"
63
- )
64
-
65
94
  if isinstance(worker_init, str):
66
95
  worker_init = worker_init.split("\n")
67
96
 
68
97
  with SudoSlurmRunner(
69
- slurm_user=slurm_user,
70
- user_cache_dir=user_cache_dir,
71
98
  root_dir_local=workflow_dir_local,
72
99
  root_dir_remote=workflow_dir_remote,
73
100
  common_script_lines=worker_init,
101
+ resource=resource,
102
+ profile=profile,
103
+ user_cache_dir=user_cache_dir,
74
104
  slurm_account=slurm_account,
75
105
  ) as runner:
76
106
  execute_tasks_v2(
@@ -88,7 +88,7 @@ def bulk_upsert_image_cache_fast(
88
88
  NOTE: we tried to replace `index_elements` with
89
89
  `constraint="pk_historyimagecache"`, but it did not work as expected.
90
90
 
91
- Arguments:
91
+ Args:
92
92
  list_upsert_objects:
93
93
  List of dictionaries for objects to be upsert-ed.
94
94
  db: A sync database session
@@ -1,15 +1,14 @@
1
- from collections.abc import Callable
2
1
  from copy import copy
3
2
  from copy import deepcopy
4
3
  from pathlib import Path
5
4
  from typing import Any
6
- from typing import Literal
7
5
 
8
6
  from sqlalchemy.orm.attributes import flag_modified
9
7
  from sqlmodel import delete
10
8
  from sqlmodel import update
11
9
 
12
10
  from .merge_outputs import merge_outputs
11
+ from .runner_functions import GetRunnerConfigType
13
12
  from .runner_functions import run_v2_task_compound
14
13
  from .runner_functions import run_v2_task_non_parallel
15
14
  from .runner_functions import run_v2_task_parallel
@@ -92,14 +91,7 @@ def execute_tasks_v2(
92
91
  job_id: int,
93
92
  workflow_dir_remote: Path | None = None,
94
93
  logger_name: str | None = None,
95
- get_runner_config: Callable[
96
- [
97
- WorkflowTaskV2,
98
- Literal["non_parallel", "parallel"],
99
- Path | None,
100
- ],
101
- Any,
102
- ],
94
+ get_runner_config: GetRunnerConfigType,
103
95
  job_type_filters: dict[str, bool],
104
96
  job_attribute_filters: AttributeFilters,
105
97
  ) -> None:
@@ -257,7 +249,7 @@ def execute_tasks_v2(
257
249
  except Exception as e:
258
250
  outcomes_dict = {
259
251
  0: SubmissionOutcome(
260
- result=None,
252
+ task_output=None,
261
253
  exception=e,
262
254
  )
263
255
  }
@@ -1,7 +1,7 @@
1
- from collections.abc import Callable
2
1
  from pathlib import Path
3
2
  from typing import Any
4
3
  from typing import Literal
4
+ from typing import Protocol
5
5
 
6
6
  from pydantic import BaseModel
7
7
  from pydantic import ConfigDict
@@ -20,7 +20,12 @@ from fractal_server.app.schemas.v2 import HistoryUnitStatus
20
20
  from fractal_server.app.schemas.v2 import TaskType
21
21
  from fractal_server.exceptions import UnreachableBranchError
22
22
  from fractal_server.logger import set_logger
23
+ from fractal_server.runner.config import JobRunnerConfigLocal
24
+ from fractal_server.runner.config import JobRunnerConfigSLURM
23
25
  from fractal_server.runner.executors.base_runner import BaseRunner
26
+ from fractal_server.runner.executors.slurm_common.slurm_config import (
27
+ SlurmConfig,
28
+ )
24
29
  from fractal_server.runner.task_files import enrich_task_files_multisubmit
25
30
  from fractal_server.runner.task_files import SUBMIT_PREFIX
26
31
  from fractal_server.runner.task_files import TaskFiles
@@ -35,6 +40,32 @@ from fractal_server.runner.v2.task_interface import (
35
40
  _cast_and_validate_TaskOutput,
36
41
  )
37
42
 
43
+
44
+ class GetRunnerConfigTypeLocal(Protocol):
45
+ def __call__(
46
+ self,
47
+ shared_config: JobRunnerConfigLocal,
48
+ wftask: WorkflowTaskV2,
49
+ which_type: Literal["non_parallel", "parallel"],
50
+ tot_tasks: int,
51
+ ) -> JobRunnerConfigLocal:
52
+ ...
53
+
54
+
55
+ class GetRunnerConfigTypeSLURM(Protocol):
56
+ def __call__(
57
+ self,
58
+ shared_config: JobRunnerConfigSLURM,
59
+ wftask: WorkflowTaskV2,
60
+ which_type: Literal["non_parallel", "parallel"],
61
+ tot_tasks: int,
62
+ ) -> SlurmConfig:
63
+ ...
64
+
65
+
66
+ GetRunnerConfigType = GetRunnerConfigTypeLocal | GetRunnerConfigTypeSLURM
67
+
68
+
38
69
  __all__ = [
39
70
  "run_v2_task_parallel",
40
71
  "run_v2_task_non_parallel",
@@ -126,15 +157,7 @@ def run_v2_task_non_parallel(
126
157
  workflow_dir_local: Path,
127
158
  workflow_dir_remote: Path,
128
159
  runner: BaseRunner,
129
- get_runner_config: Callable[
130
- [
131
- WorkflowTaskV2,
132
- Literal["non_parallel", "parallel"],
133
- Path | None,
134
- int,
135
- ],
136
- Any,
137
- ],
160
+ get_runner_config: GetRunnerConfigType,
138
161
  dataset_id: int,
139
162
  history_run_id: int,
140
163
  task_type: Literal[TaskType.NON_PARALLEL, TaskType.CONVERTER_NON_PARALLEL],
@@ -163,8 +186,10 @@ def run_v2_task_non_parallel(
163
186
  )
164
187
 
165
188
  runner_config = get_runner_config(
189
+ shared_config=runner.shared_config,
166
190
  wftask=wftask,
167
191
  which_type="non_parallel",
192
+ tot_tasks=1,
168
193
  )
169
194
 
170
195
  function_kwargs = {
@@ -251,15 +276,7 @@ def run_v2_task_parallel(
251
276
  runner: BaseRunner,
252
277
  workflow_dir_local: Path,
253
278
  workflow_dir_remote: Path,
254
- get_runner_config: Callable[
255
- [
256
- WorkflowTaskV2,
257
- Literal["non_parallel", "parallel"],
258
- Path | None,
259
- int,
260
- ],
261
- Any,
262
- ],
279
+ get_runner_config: GetRunnerConfigType,
263
280
  dataset_id: int,
264
281
  history_run_id: int,
265
282
  user_id: int,
@@ -278,6 +295,7 @@ def run_v2_task_parallel(
278
295
  )
279
296
 
280
297
  runner_config = get_runner_config(
298
+ shared_config=runner.shared_config,
281
299
  wftask=wftask,
282
300
  which_type="parallel",
283
301
  tot_tasks=len(images),
@@ -382,15 +400,7 @@ def run_v2_task_compound(
382
400
  runner: BaseRunner,
383
401
  workflow_dir_local: Path,
384
402
  workflow_dir_remote: Path,
385
- get_runner_config: Callable[
386
- [
387
- WorkflowTaskV2,
388
- Literal["non_parallel", "parallel"],
389
- Path | None,
390
- int,
391
- ],
392
- Any,
393
- ],
403
+ get_runner_config: GetRunnerConfigType,
394
404
  dataset_id: int,
395
405
  history_run_id: int,
396
406
  task_type: Literal[TaskType.COMPOUND, TaskType.CONVERTER_COMPOUND],
@@ -407,9 +417,12 @@ def run_v2_task_compound(
407
417
  )
408
418
 
409
419
  runner_config_init = get_runner_config(
420
+ shared_config=runner.shared_config,
410
421
  wftask=wftask,
411
422
  which_type="non_parallel",
423
+ tot_tasks=1,
412
424
  )
425
+
413
426
  # 3/A: non-parallel init task
414
427
  function_kwargs = {
415
428
  "zarr_dir": zarr_dir,
@@ -505,6 +518,7 @@ def run_v2_task_compound(
505
518
  return init_outcome, num_tasks
506
519
 
507
520
  runner_config_compute = get_runner_config(
521
+ shared_config=runner.shared_config,
508
522
  wftask=wftask,
509
523
  which_type="parallel",
510
524
  tot_tasks=len(parallelization_list),