fractal-server 2.16.6__py3-none-any.whl → 2.17.0__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 (142) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +178 -52
  3. fractal_server/app/db/__init__.py +9 -11
  4. fractal_server/app/models/security.py +30 -22
  5. fractal_server/app/models/user_settings.py +5 -4
  6. fractal_server/app/models/v2/__init__.py +4 -0
  7. fractal_server/app/models/v2/profile.py +16 -0
  8. fractal_server/app/models/v2/project.py +5 -0
  9. fractal_server/app/models/v2/resource.py +130 -0
  10. fractal_server/app/models/v2/task_group.py +4 -0
  11. fractal_server/app/routes/admin/v2/__init__.py +4 -0
  12. fractal_server/app/routes/admin/v2/_aux_functions.py +55 -0
  13. fractal_server/app/routes/admin/v2/accounting.py +3 -3
  14. fractal_server/app/routes/admin/v2/impersonate.py +2 -2
  15. fractal_server/app/routes/admin/v2/job.py +51 -15
  16. fractal_server/app/routes/admin/v2/profile.py +100 -0
  17. fractal_server/app/routes/admin/v2/project.py +2 -2
  18. fractal_server/app/routes/admin/v2/resource.py +222 -0
  19. fractal_server/app/routes/admin/v2/task.py +59 -32
  20. fractal_server/app/routes/admin/v2/task_group.py +17 -12
  21. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +52 -86
  22. fractal_server/app/routes/api/__init__.py +45 -8
  23. fractal_server/app/routes/api/v2/_aux_functions.py +17 -1
  24. fractal_server/app/routes/api/v2/_aux_functions_history.py +2 -2
  25. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
  26. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +55 -19
  27. fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +21 -17
  28. fractal_server/app/routes/api/v2/dataset.py +10 -19
  29. fractal_server/app/routes/api/v2/history.py +8 -8
  30. fractal_server/app/routes/api/v2/images.py +5 -5
  31. fractal_server/app/routes/api/v2/job.py +8 -8
  32. fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
  33. fractal_server/app/routes/api/v2/project.py +15 -7
  34. fractal_server/app/routes/api/v2/status_legacy.py +2 -2
  35. fractal_server/app/routes/api/v2/submit.py +49 -42
  36. fractal_server/app/routes/api/v2/task.py +26 -8
  37. fractal_server/app/routes/api/v2/task_collection.py +39 -50
  38. fractal_server/app/routes/api/v2/task_collection_custom.py +10 -6
  39. fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -42
  40. fractal_server/app/routes/api/v2/task_group.py +19 -9
  41. fractal_server/app/routes/api/v2/task_group_lifecycle.py +43 -86
  42. fractal_server/app/routes/api/v2/task_version_update.py +3 -3
  43. fractal_server/app/routes/api/v2/workflow.py +9 -9
  44. fractal_server/app/routes/api/v2/workflow_import.py +25 -13
  45. fractal_server/app/routes/api/v2/workflowtask.py +5 -5
  46. fractal_server/app/routes/auth/__init__.py +34 -5
  47. fractal_server/app/routes/auth/_aux_auth.py +39 -20
  48. fractal_server/app/routes/auth/current_user.py +56 -67
  49. fractal_server/app/routes/auth/group.py +29 -46
  50. fractal_server/app/routes/auth/oauth.py +55 -38
  51. fractal_server/app/routes/auth/register.py +2 -2
  52. fractal_server/app/routes/auth/router.py +4 -2
  53. fractal_server/app/routes/auth/users.py +29 -53
  54. fractal_server/app/routes/aux/_runner.py +2 -1
  55. fractal_server/app/routes/aux/validate_user_profile.py +62 -0
  56. fractal_server/app/schemas/__init__.py +0 -1
  57. fractal_server/app/schemas/user.py +43 -13
  58. fractal_server/app/schemas/user_group.py +2 -1
  59. fractal_server/app/schemas/v2/__init__.py +12 -0
  60. fractal_server/app/schemas/v2/profile.py +78 -0
  61. fractal_server/app/schemas/v2/resource.py +137 -0
  62. fractal_server/app/schemas/v2/task_collection.py +11 -3
  63. fractal_server/app/schemas/v2/task_group.py +5 -0
  64. fractal_server/app/security/__init__.py +174 -75
  65. fractal_server/app/security/signup_email.py +52 -34
  66. fractal_server/config/__init__.py +27 -0
  67. fractal_server/config/_data.py +68 -0
  68. fractal_server/config/_database.py +59 -0
  69. fractal_server/config/_email.py +133 -0
  70. fractal_server/config/_main.py +78 -0
  71. fractal_server/config/_oauth.py +69 -0
  72. fractal_server/config/_settings_config.py +7 -0
  73. fractal_server/data_migrations/2_17_0.py +339 -0
  74. fractal_server/images/tools.py +3 -3
  75. fractal_server/logger.py +3 -3
  76. fractal_server/main.py +17 -23
  77. fractal_server/migrations/naming_convention.py +1 -1
  78. fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py +195 -0
  79. fractal_server/runner/config/__init__.py +2 -0
  80. fractal_server/runner/config/_local.py +21 -0
  81. fractal_server/runner/config/_slurm.py +129 -0
  82. fractal_server/runner/config/slurm_mem_to_MB.py +63 -0
  83. fractal_server/runner/exceptions.py +4 -0
  84. fractal_server/runner/executors/base_runner.py +17 -7
  85. fractal_server/runner/executors/local/get_local_config.py +21 -86
  86. fractal_server/runner/executors/local/runner.py +48 -5
  87. fractal_server/runner/executors/slurm_common/_batching.py +2 -2
  88. fractal_server/runner/executors/slurm_common/base_slurm_runner.py +60 -26
  89. fractal_server/runner/executors/slurm_common/get_slurm_config.py +39 -55
  90. fractal_server/runner/executors/slurm_common/remote.py +1 -1
  91. fractal_server/runner/executors/slurm_common/slurm_config.py +214 -0
  92. fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -1
  93. fractal_server/runner/executors/slurm_ssh/runner.py +12 -14
  94. fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +2 -2
  95. fractal_server/runner/executors/slurm_sudo/runner.py +12 -12
  96. fractal_server/runner/v2/_local.py +36 -21
  97. fractal_server/runner/v2/_slurm_ssh.py +41 -4
  98. fractal_server/runner/v2/_slurm_sudo.py +42 -12
  99. fractal_server/runner/v2/db_tools.py +1 -1
  100. fractal_server/runner/v2/runner.py +3 -11
  101. fractal_server/runner/v2/runner_functions.py +42 -28
  102. fractal_server/runner/v2/submit_workflow.py +88 -109
  103. fractal_server/runner/versions.py +8 -3
  104. fractal_server/ssh/_fabric.py +6 -6
  105. fractal_server/tasks/config/__init__.py +3 -0
  106. fractal_server/tasks/config/_pixi.py +127 -0
  107. fractal_server/tasks/config/_python.py +51 -0
  108. fractal_server/tasks/v2/local/_utils.py +7 -7
  109. fractal_server/tasks/v2/local/collect.py +13 -5
  110. fractal_server/tasks/v2/local/collect_pixi.py +26 -10
  111. fractal_server/tasks/v2/local/deactivate.py +7 -1
  112. fractal_server/tasks/v2/local/deactivate_pixi.py +5 -1
  113. fractal_server/tasks/v2/local/delete.py +5 -1
  114. fractal_server/tasks/v2/local/reactivate.py +13 -5
  115. fractal_server/tasks/v2/local/reactivate_pixi.py +27 -9
  116. fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py +11 -10
  117. fractal_server/tasks/v2/ssh/_utils.py +6 -7
  118. fractal_server/tasks/v2/ssh/collect.py +19 -12
  119. fractal_server/tasks/v2/ssh/collect_pixi.py +34 -16
  120. fractal_server/tasks/v2/ssh/deactivate.py +12 -8
  121. fractal_server/tasks/v2/ssh/deactivate_pixi.py +14 -10
  122. fractal_server/tasks/v2/ssh/delete.py +12 -9
  123. fractal_server/tasks/v2/ssh/reactivate.py +18 -12
  124. fractal_server/tasks/v2/ssh/reactivate_pixi.py +36 -17
  125. fractal_server/tasks/v2/templates/4_pip_show.sh +4 -6
  126. fractal_server/tasks/v2/utils_database.py +2 -2
  127. fractal_server/tasks/v2/utils_pixi.py +3 -0
  128. fractal_server/tasks/v2/utils_python_interpreter.py +8 -16
  129. fractal_server/tasks/v2/utils_templates.py +7 -10
  130. fractal_server/utils.py +1 -1
  131. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/METADATA +4 -6
  132. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/RECORD +136 -117
  133. fractal_server/app/routes/aux/validate_user_settings.py +0 -73
  134. fractal_server/app/schemas/user_settings.py +0 -67
  135. fractal_server/app/user_settings.py +0 -42
  136. fractal_server/config.py +0 -906
  137. fractal_server/data_migrations/2_14_10.py +0 -48
  138. fractal_server/runner/executors/slurm_common/_slurm_config.py +0 -471
  139. /fractal_server/{runner → app}/shutdown.py +0 -0
  140. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/WHEEL +0 -0
  141. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/entry_points.txt +0 -0
  142. {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,48 +0,0 @@
1
- import logging
2
-
3
- from sqlmodel import select
4
-
5
- from fractal_server.app.db import get_sync_db
6
- from fractal_server.app.models import HistoryRun
7
- from fractal_server.app.models import TaskV2
8
- from fractal_server.app.models import WorkflowTaskV2
9
-
10
- logger = logging.getLogger("fix_db")
11
- logger.setLevel(logging.INFO)
12
-
13
-
14
- def fix_db():
15
- logger.info("START execution of fix_db function")
16
-
17
- with next(get_sync_db()) as db:
18
- stm = select(HistoryRun).order_by(HistoryRun.id)
19
- history_runs = db.execute(stm).scalars().all()
20
-
21
- for hr in history_runs:
22
- logger.info(f"HistoryRun[{hr.id}] START")
23
- if hr.workflowtask_id is None:
24
- continue
25
- wft = db.get(WorkflowTaskV2, hr.workflowtask_id)
26
- if wft is None:
27
- logger.warning(
28
- f"WorkflowTaskV2[{hr.workflowtask_id}] not found. "
29
- "Trying to use HistoryRun.workflowtask_dump"
30
- )
31
- task_id = hr.workflowtask_dump.get("task_id")
32
- if task_id is not None and db.get(TaskV2, task_id) is not None:
33
- hr.task_id = task_id
34
- else:
35
- logger.warning(f"TaskV2[{task_id}] not found")
36
- else:
37
- hr.task_id = wft.task_id
38
- logger.info(
39
- f"HistoryRun[{hr.id}].task_id set to {wft.task_id}"
40
- )
41
-
42
- db.add(hr)
43
- logger.info(f"HistoryRun[{hr.id}] END")
44
-
45
- db.commit()
46
- logger.info("Changes committed.")
47
-
48
- logger.info("END execution of fix_db function")
@@ -1,471 +0,0 @@
1
- # Copyright 2022 (C) Friedrich Miescher Institute for Biomedical Research and
2
- # University of Zurich
3
- #
4
- # Original authors:
5
- # Jacopo Nespolo <jacopo.nespolo@exact-lab.it>
6
- # Tommaso Comparin <tommaso.comparin@exact-lab.it>
7
- #
8
- # This file is part of Fractal and was originally developed by eXact lab S.r.l.
9
- # <exact-lab.it> under contract with Liberali Lab from the Friedrich Miescher
10
- # Institute for Biomedical Research and Pelkmans Lab from the University of
11
- # Zurich.
12
- """
13
- Submodule to handle the SLURM configuration for a WorkflowTask
14
- """
15
- import json
16
- from pathlib import Path
17
-
18
- from pydantic import BaseModel
19
- from pydantic import ConfigDict
20
- from pydantic import Field
21
- from pydantic import ValidationError
22
-
23
- from fractal_server.config import get_settings
24
- from fractal_server.logger import set_logger
25
- from fractal_server.syringe import Inject
26
-
27
- logger = set_logger(__name__)
28
-
29
-
30
- class SlurmConfigError(ValueError):
31
- """
32
- Slurm configuration error
33
- """
34
-
35
- pass
36
-
37
-
38
- class _SlurmConfigSet(BaseModel):
39
- """
40
- Options that can be set in `FRACTAL_SLURM_CONFIG_FILE` for the default/gpu
41
- SLURM config. Only used as part of `SlurmConfigFile`.
42
-
43
- Attributes:
44
- partition:
45
- cpus_per_task:
46
- mem:
47
- See `_parse_mem_value` for details on allowed values.
48
- constraint:
49
- gres:
50
- time:
51
- exclude:
52
- nodelist:
53
- account:
54
- extra_lines:
55
- """
56
-
57
- model_config = ConfigDict(extra="forbid")
58
-
59
- partition: str | None = None
60
- cpus_per_task: int | None = None
61
- mem: int | str | None = None
62
- constraint: str | None = None
63
- gres: str | None = None
64
- exclude: str | None = None
65
- nodelist: str | None = None
66
- time: str | None = None
67
- account: str | None = None
68
- extra_lines: list[str] | None = None
69
- gpus: str | None = None
70
-
71
-
72
- class _BatchingConfigSet(BaseModel):
73
- """
74
- Options that can be set in `FRACTAL_SLURM_CONFIG_FILE` to configure the
75
- batching strategy (that is, how to combine several tasks in a single SLURM
76
- job). Only used as part of `SlurmConfigFile`.
77
-
78
- Attributes:
79
- target_cpus_per_job:
80
- max_cpus_per_job:
81
- target_mem_per_job:
82
- (see `_parse_mem_value` for details on allowed values)
83
- max_mem_per_job:
84
- (see `_parse_mem_value` for details on allowed values)
85
- target_num_jobs:
86
- max_num_jobs:
87
- """
88
-
89
- model_config = ConfigDict(extra="forbid")
90
-
91
- target_cpus_per_job: int
92
- max_cpus_per_job: int
93
- target_mem_per_job: int | str
94
- max_mem_per_job: int | str
95
- target_num_jobs: int
96
- max_num_jobs: int
97
-
98
-
99
- class SlurmConfigFile(BaseModel):
100
- """
101
- Specifications for the content of `FRACTAL_SLURM_CONFIG_FILE`
102
-
103
- This must be a JSON file, and a valid example is
104
- ```JSON
105
- {
106
- "default_slurm_config": {
107
- "partition": "main",
108
- "cpus_per_task": 1
109
- },
110
- "gpu_slurm_config": {
111
- "partition": "gpu",
112
- "extra_lines": ["#SBATCH --gres=gpu:v100:1"]
113
- },
114
- "batching_config": {
115
- "target_cpus_per_job": 1,
116
- "max_cpus_per_job": 1,
117
- "target_mem_per_job": 200,
118
- "max_mem_per_job": 500,
119
- "target_num_jobs": 2,
120
- "max_num_jobs": 4
121
- },
122
- "user_local_exports": {
123
- "CELLPOSE_LOCAL_MODELS_PATH": "CELLPOSE_LOCAL_MODELS_PATH",
124
- "NAPARI_CONFIG": "napari_config.json"
125
- }
126
- }
127
- ```
128
-
129
- See `_SlurmConfigSet` and `_BatchingConfigSet` for more details.
130
-
131
- Attributes:
132
- default_slurm_config:
133
- Common default options for all tasks.
134
- gpu_slurm_config:
135
- Default configuration for all GPU tasks.
136
- batching_config:
137
- Configuration of the batching strategy.
138
- user_local_exports:
139
- Key-value pairs to be included as `export`-ed variables in SLURM
140
- submission script, after prepending values with the user's cache
141
- directory.
142
- """
143
-
144
- model_config = ConfigDict(extra="forbid")
145
-
146
- default_slurm_config: _SlurmConfigSet
147
- gpu_slurm_config: _SlurmConfigSet | None = None
148
- batching_config: _BatchingConfigSet
149
- user_local_exports: dict[str, str] | None = None
150
-
151
-
152
- def load_slurm_config_file(
153
- config_path: Path | None = None,
154
- ) -> SlurmConfigFile:
155
- """
156
- Load a SLURM configuration file and validate its content with
157
- `SlurmConfigFile`.
158
-
159
- Arguments:
160
- config_path:
161
- """
162
-
163
- if not config_path:
164
- settings = Inject(get_settings)
165
- config_path = settings.FRACTAL_SLURM_CONFIG_FILE
166
-
167
- # Load file
168
- logger.debug(f"[get_slurm_config] Now loading {config_path=}")
169
- try:
170
- with config_path.open("r") as f:
171
- slurm_env = json.load(f)
172
- except Exception as e:
173
- raise SlurmConfigError(
174
- f"Error while loading {config_path=}. "
175
- f"Original error:\n{str(e)}"
176
- )
177
-
178
- # Validate file content
179
- logger.debug(f"[load_slurm_config_file] Now validating {config_path=}")
180
- logger.debug(f"[load_slurm_config_file] {slurm_env=}")
181
- try:
182
- obj = SlurmConfigFile(**slurm_env)
183
- except ValidationError as e:
184
- raise SlurmConfigError(
185
- f"Error while loading {config_path=}. "
186
- f"Original error:\n{str(e)}"
187
- )
188
-
189
- # Convert memory to MB units, in all relevant attributes
190
- if obj.default_slurm_config.mem:
191
- obj.default_slurm_config.mem = _parse_mem_value(
192
- obj.default_slurm_config.mem
193
- )
194
- if obj.gpu_slurm_config and obj.gpu_slurm_config.mem:
195
- obj.gpu_slurm_config.mem = _parse_mem_value(obj.gpu_slurm_config.mem)
196
- obj.batching_config.target_mem_per_job = _parse_mem_value(
197
- obj.batching_config.target_mem_per_job
198
- )
199
- obj.batching_config.max_mem_per_job = _parse_mem_value(
200
- obj.batching_config.max_mem_per_job
201
- )
202
-
203
- return obj
204
-
205
-
206
- class SlurmConfig(BaseModel):
207
- """
208
- Abstraction for SLURM parameters
209
-
210
- **NOTE**: `SlurmConfig` objects are created internally in `fractal-server`,
211
- and they are not meant to be initialized by the user; the same holds for
212
- `SlurmConfig` attributes (e.g. `mem_per_task_MB`), which are not meant to
213
- be part of the `FRACTAL_SLURM_CONFIG_FILE` JSON file (details on the
214
- expected file content are defined in
215
- [`SlurmConfigFile`](#fractal_server.runner._slurm._slurm_config.SlurmConfigFile)).
216
-
217
- Part of the attributes map directly to some of the SLURM attributes (see
218
- https://slurm.schedmd.com/sbatch.html), e.g. `partition`. Other attributes
219
- are metaparameters which are needed in fractal-server to combine multiple
220
- tasks in the same SLURM job (e.g. `parallel_tasks_per_job` or
221
- `max_num_jobs`).
222
-
223
- Attributes:
224
- partition: Corresponds to SLURM option.
225
- cpus_per_task: Corresponds to SLURM option.
226
- mem_per_task_MB: Corresponds to `mem` SLURM option.
227
- job_name: Corresponds to `name` SLURM option.
228
- constraint: Corresponds to SLURM option.
229
- gres: Corresponds to SLURM option.
230
- account: Corresponds to SLURM option.
231
- gpus: Corresponds to SLURM option.
232
- time: Corresponds to SLURM option (WARNING: not fully supported).
233
- nodelist: Corresponds to SLURM option.
234
- exclude: Corresponds to SLURM option.
235
- prefix: Prefix of configuration lines in SLURM submission scripts.
236
- shebang_line: Shebang line for SLURM submission scripts.
237
- extra_lines: Additional lines to include in SLURM submission scripts.
238
- tasks_per_job: Number of tasks for each SLURM job.
239
- parallel_tasks_per_job: Number of tasks to run in parallel for
240
- each SLURM job.
241
- target_cpus_per_job: Optimal number of CPUs to be requested in each
242
- SLURM job.
243
- max_cpus_per_job: Maximum number of CPUs that can be requested in each
244
- SLURM job.
245
- target_mem_per_job: Optimal amount of memory (in MB) to be requested in
246
- each SLURM job.
247
- max_mem_per_job: Maximum amount of memory (in MB) that can be requested
248
- in each SLURM job.
249
- target_num_jobs: Optimal number of SLURM jobs for a given WorkflowTask.
250
- max_num_jobs: Maximum number of SLURM jobs for a given WorkflowTask.
251
- user_local_exports:
252
- Key-value pairs to be included as `export`-ed variables in SLURM
253
- submission script, after prepending values with the user's cache
254
- directory.
255
- """
256
-
257
- model_config = ConfigDict(extra="forbid")
258
-
259
- # Required SLURM parameters (note that the integer attributes are those
260
- # that will need to scale up with the number of parallel tasks per job)
261
- partition: str
262
- cpus_per_task: int
263
- mem_per_task_MB: int
264
- prefix: str = "#SBATCH"
265
- shebang_line: str = "#!/bin/sh"
266
-
267
- # Optional SLURM parameters
268
- job_name: str | None = None
269
- constraint: str | None = None
270
- gres: str | None = None
271
- gpus: str | None = None
272
- time: str | None = None
273
- account: str | None = None
274
- nodelist: str | None = None
275
- exclude: str | None = None
276
-
277
- # Free-field attribute for extra lines to be added to the SLURM job
278
- # preamble
279
- extra_lines: list[str] | None = Field(default_factory=list)
280
-
281
- # Variables that will be `export`ed in the SLURM submission script
282
- user_local_exports: dict[str, str] | None = None
283
-
284
- # Metaparameters needed to combine multiple tasks in each SLURM job
285
- tasks_per_job: int | None = None
286
- parallel_tasks_per_job: int | None = None
287
- target_cpus_per_job: int
288
- max_cpus_per_job: int
289
- target_mem_per_job: int
290
- max_mem_per_job: int
291
- target_num_jobs: int
292
- max_num_jobs: int
293
-
294
- def _sorted_extra_lines(self) -> list[str]:
295
- """
296
- Return a copy of `self.extra_lines`, where lines starting with
297
- `self.prefix` are listed first.
298
- """
299
-
300
- def _no_prefix(_line):
301
- if _line.startswith(self.prefix):
302
- return 0
303
- else:
304
- return 1
305
-
306
- return sorted(self.extra_lines, key=_no_prefix)
307
-
308
- def sort_script_lines(self, script_lines: list[str]) -> list[str]:
309
- """
310
- Return a copy of `script_lines`, where lines are sorted as in:
311
-
312
- 1. `self.shebang_line` (if present);
313
- 2. Lines starting with `self.prefix`;
314
- 3. Other lines.
315
-
316
- Arguments:
317
- script_lines:
318
- """
319
-
320
- def _sorting_function(_line):
321
- if _line == self.shebang_line:
322
- return 0
323
- elif _line.startswith(self.prefix):
324
- return 1
325
- else:
326
- return 2
327
-
328
- return sorted(script_lines, key=_sorting_function)
329
-
330
- def to_sbatch_preamble(
331
- self,
332
- remote_export_dir: str | None = None,
333
- ) -> list[str]:
334
- """
335
- Compile `SlurmConfig` object into the preamble of a SLURM submission
336
- script.
337
-
338
- Arguments:
339
- remote_export_dir:
340
- Base directory for exports defined in
341
- `self.user_local_exports`.
342
- """
343
- if self.parallel_tasks_per_job is None:
344
- raise ValueError(
345
- "SlurmConfig.sbatch_preamble requires that "
346
- f"{self.parallel_tasks_per_job=} is not None."
347
- )
348
- if self.extra_lines:
349
- if len(self.extra_lines) != len(set(self.extra_lines)):
350
- raise ValueError(f"{self.extra_lines=} contains repetitions")
351
-
352
- mem_per_job_MB = self.parallel_tasks_per_job * self.mem_per_task_MB
353
- lines = [
354
- self.shebang_line,
355
- f"{self.prefix} --partition={self.partition}",
356
- f"{self.prefix} --ntasks={self.parallel_tasks_per_job}",
357
- f"{self.prefix} --cpus-per-task={self.cpus_per_task}",
358
- f"{self.prefix} --mem={mem_per_job_MB}M",
359
- ]
360
- for key in [
361
- "job_name",
362
- "constraint",
363
- "gres",
364
- "gpus",
365
- "time",
366
- "account",
367
- "exclude",
368
- "nodelist",
369
- ]:
370
- value = getattr(self, key)
371
- if value is not None:
372
- # Handle the `time` parameter
373
- if key == "time" and self.parallel_tasks_per_job > 1:
374
- # NOTE: see issue #1632
375
- logger.warning(
376
- f"`time` SLURM parameter is set to {self.time}, "
377
- "but this does not take into account the number of "
378
- f"SLURM tasks ({self.parallel_tasks_per_job})."
379
- )
380
- option = key.replace("_", "-")
381
- lines.append(f"{self.prefix} --{option}={value}")
382
-
383
- if self.extra_lines:
384
- for line in self._sorted_extra_lines():
385
- lines.append(line)
386
-
387
- if self.user_local_exports:
388
- if remote_export_dir is None:
389
- raise ValueError(
390
- f"remote_export_dir=None but {self.user_local_exports=}"
391
- )
392
- for key, value in self.user_local_exports.items():
393
- tmp_value = str(Path(remote_export_dir) / value)
394
- lines.append(f"export {key}={tmp_value}")
395
-
396
- """
397
- FIXME export SRUN_CPUS_PER_TASK
398
- # From https://slurm.schedmd.com/sbatch.html: Beginning with 22.05,
399
- # srun will not inherit the --cpus-per-task value requested by salloc
400
- # or sbatch. It must be requested again with the call to srun or set
401
- # with the SRUN_CPUS_PER_TASK environment variable if desired for the
402
- # task(s).
403
- if config.cpus_per_task:
404
- #additional_setup_lines.append(
405
- f"export SRUN_CPUS_PER_TASK={config.cpus_per_task}"
406
- )
407
- """
408
-
409
- return lines
410
-
411
- @property
412
- def batch_size(self) -> int:
413
- return self.tasks_per_job
414
-
415
-
416
- def _parse_mem_value(raw_mem: str | int) -> int:
417
- """
418
- Convert a memory-specification string into an integer (in MB units), or
419
- simply return the input if it is already an integer.
420
-
421
- Supported units are `"M", "G", "T"`, with `"M"` being the default; some
422
- parsing examples are: `"10M" -> 10000`, `"3G" -> 3000000`.
423
-
424
- Arguments:
425
- raw_mem:
426
- A string (e.g. `"100M"`) or an integer (in MB).
427
-
428
- Returns:
429
- Integer value of memory in MB units.
430
- """
431
-
432
- info = f"[_parse_mem_value] {raw_mem=}"
433
- error_msg = (
434
- f"{info}, invalid specification of memory requirements "
435
- "(valid examples: 93, 71M, 93G, 71T)."
436
- )
437
-
438
- # Handle integer argument
439
- if type(raw_mem) is int:
440
- return raw_mem
441
-
442
- # Handle string argument
443
- if not raw_mem[0].isdigit(): # fail e.g. for raw_mem="M100"
444
- logger.error(error_msg)
445
- raise SlurmConfigError(error_msg)
446
- if raw_mem.isdigit():
447
- mem_MB = int(raw_mem)
448
- elif raw_mem.endswith("M"):
449
- stripped_raw_mem = raw_mem.strip("M")
450
- if not stripped_raw_mem.isdigit():
451
- logger.error(error_msg)
452
- raise SlurmConfigError(error_msg)
453
- mem_MB = int(stripped_raw_mem)
454
- elif raw_mem.endswith("G"):
455
- stripped_raw_mem = raw_mem.strip("G")
456
- if not stripped_raw_mem.isdigit():
457
- logger.error(error_msg)
458
- raise SlurmConfigError(error_msg)
459
- mem_MB = int(stripped_raw_mem) * 10**3
460
- elif raw_mem.endswith("T"):
461
- stripped_raw_mem = raw_mem.strip("T")
462
- if not stripped_raw_mem.isdigit():
463
- logger.error(error_msg)
464
- raise SlurmConfigError(error_msg)
465
- mem_MB = int(stripped_raw_mem) * 10**6
466
- else:
467
- logger.error(error_msg)
468
- raise SlurmConfigError(error_msg)
469
-
470
- logger.debug(f"{info}, return {mem_MB}")
471
- return mem_MB
File without changes