fractal-server 2.0.6__py3-none-any.whl → 2.2.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 (47) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/routes/admin/v1.py +2 -4
  3. fractal_server/app/routes/admin/v2.py +2 -4
  4. fractal_server/app/routes/api/v1/_aux_functions.py +24 -0
  5. fractal_server/app/routes/api/v1/job.py +3 -4
  6. fractal_server/app/routes/api/v1/project.py +28 -18
  7. fractal_server/app/routes/api/v2/_aux_functions.py +35 -12
  8. fractal_server/app/routes/api/v2/job.py +3 -4
  9. fractal_server/app/routes/api/v2/project.py +21 -0
  10. fractal_server/app/routes/api/v2/submit.py +36 -15
  11. fractal_server/app/routes/aux/_job.py +3 -1
  12. fractal_server/app/routes/aux/_runner.py +3 -3
  13. fractal_server/app/runner/executors/slurm/executor.py +169 -68
  14. fractal_server/app/runner/shutdown.py +88 -0
  15. fractal_server/app/runner/task_files.py +59 -27
  16. fractal_server/app/runner/v1/__init__.py +38 -27
  17. fractal_server/app/runner/v1/_common.py +53 -51
  18. fractal_server/app/runner/v1/_local/__init__.py +12 -11
  19. fractal_server/app/runner/v1/_local/_submit_setup.py +4 -4
  20. fractal_server/app/runner/v1/_slurm/__init__.py +16 -16
  21. fractal_server/app/runner/v1/_slurm/_submit_setup.py +11 -10
  22. fractal_server/app/runner/v1/_slurm/get_slurm_config.py +6 -6
  23. fractal_server/app/runner/v2/__init__.py +47 -15
  24. fractal_server/app/runner/v2/_local/__init__.py +12 -11
  25. fractal_server/app/runner/v2/_local/_local_config.py +1 -1
  26. fractal_server/app/runner/v2/_local/_submit_setup.py +4 -4
  27. fractal_server/app/runner/v2/_local_experimental/__init__.py +145 -0
  28. fractal_server/app/runner/v2/_local_experimental/_local_config.py +108 -0
  29. fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +42 -0
  30. fractal_server/app/runner/v2/_local_experimental/executor.py +152 -0
  31. fractal_server/app/runner/v2/_slurm/__init__.py +10 -10
  32. fractal_server/app/runner/v2/_slurm/_submit_setup.py +11 -10
  33. fractal_server/app/runner/v2/_slurm/get_slurm_config.py +6 -6
  34. fractal_server/app/runner/v2/runner.py +17 -15
  35. fractal_server/app/runner/v2/runner_functions.py +38 -38
  36. fractal_server/app/runner/v2/runner_functions_low_level.py +12 -6
  37. fractal_server/app/security/__init__.py +4 -5
  38. fractal_server/config.py +35 -1
  39. fractal_server/gunicorn_fractal.py +40 -0
  40. fractal_server/{logger/__init__.py → logger.py} +2 -2
  41. fractal_server/main.py +45 -26
  42. {fractal_server-2.0.6.dist-info → fractal_server-2.2.0a0.dist-info}/METADATA +1 -1
  43. {fractal_server-2.0.6.dist-info → fractal_server-2.2.0a0.dist-info}/RECORD +46 -41
  44. fractal_server/logger/gunicorn_logger.py +0 -19
  45. {fractal_server-2.0.6.dist-info → fractal_server-2.2.0a0.dist-info}/LICENSE +0 -0
  46. {fractal_server-2.0.6.dist-info → fractal_server-2.2.0a0.dist-info}/WHEEL +0 -0
  47. {fractal_server-2.0.6.dist-info → fractal_server-2.2.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,32 +1,55 @@
1
1
  from pathlib import Path
2
2
  from typing import Optional
3
+ from typing import Union
4
+
5
+ from fractal_server.tasks.utils import slugify_task_name
3
6
 
4
7
 
5
8
  def sanitize_component(value: str) -> str:
6
9
  """
7
10
  Remove {" ", "/", "."} form a string, e.g. going from
8
11
  'plate.zarr/B/03/0' to 'plate_zarr_B_03_0'.
12
+
13
+ Args:
14
+ value: Input strig
9
15
  """
10
16
  return value.replace(" ", "_").replace("/", "_").replace(".", "_")
11
17
 
12
18
 
19
+ def task_subfolder_name(order: Union[int, str], task_name: str) -> str:
20
+ """
21
+ Get name of task-specific subfolder.
22
+
23
+ Args:
24
+ order:
25
+ task_name:
26
+ """
27
+ task_name_slug = slugify_task_name(task_name)
28
+ return f"{order}_{task_name_slug}"
29
+
30
+
13
31
  class TaskFiles:
14
32
  """
15
33
  Group all file paths pertaining to a task
16
34
 
17
35
  Attributes:
18
- workflow_dir:
36
+ workflow_dir_local:
19
37
  Server-owned directory to store all task-execution-related relevant
20
- files (inputs, outputs, errors, and all meta files related to the
21
- job execution). Note: users cannot write directly to this folder.
22
- workflow_dir_user:
23
- User-side directory with the same scope as `workflow_dir`, and
24
- where a user can write.
38
+ files. Note: users cannot write directly to this folder.
39
+ workflow_dir_remote:
40
+ User-side directory with the same scope as `workflow_dir_local`,
41
+ and where a user can write.
42
+ subfolder_name:
43
+ Name of task-specific subfolder
44
+ remote_subfolder:
45
+ Path to user-side task-specific subfolder
46
+ task_name:
47
+ Name of the task
25
48
  task_order:
26
49
  Positional order of the task within a workflow.
27
50
  component:
28
- Specific component to run the task for (relevant for tasks that
29
- will be executed in parallel over many components).
51
+ Specific component to run the task for (relevant for tasks to be
52
+ executed in parallel over many components).
30
53
  file_prefix:
31
54
  Prefix for all task-related files.
32
55
  args:
@@ -39,12 +62,16 @@ class TaskFiles:
39
62
  Path for task-execution stderr.
40
63
  """
41
64
 
42
- workflow_dir: Path
43
- workflow_dir_user: Path
65
+ workflow_dir_local: Path
66
+ workflow_dir_remote: Path
67
+ remote_subfolder: Path
68
+ subfolder_name: str
69
+ task_name: str
44
70
  task_order: Optional[int] = None
45
71
  component: Optional[str] = None
46
72
 
47
73
  file_prefix: str
74
+ file_prefix_with_subfolder: str
48
75
  args: Path
49
76
  out: Path
50
77
  err: Path
@@ -53,14 +80,16 @@ class TaskFiles:
53
80
 
54
81
  def __init__(
55
82
  self,
56
- workflow_dir: Path,
57
- workflow_dir_user: Path,
83
+ workflow_dir_local: Path,
84
+ workflow_dir_remote: Path,
85
+ task_name: str,
58
86
  task_order: Optional[int] = None,
59
87
  component: Optional[str] = None,
60
88
  ):
61
- self.workflow_dir = workflow_dir
62
- self.workflow_dir_user = workflow_dir_user
89
+ self.workflow_dir_local = workflow_dir_local
90
+ self.workflow_dir_remote = workflow_dir_remote
63
91
  self.task_order = task_order
92
+ self.task_name = task_name
64
93
  self.component = component
65
94
 
66
95
  if self.component is not None:
@@ -72,32 +101,35 @@ class TaskFiles:
72
101
  if self.task_order is not None:
73
102
  order = str(self.task_order)
74
103
  else:
75
- order = "task"
104
+ order = "0"
76
105
  self.file_prefix = f"{order}{component_safe}"
77
- self.args = self.workflow_dir_user / f"{self.file_prefix}.args.json"
78
- self.out = self.workflow_dir_user / f"{self.file_prefix}.out"
79
- self.err = self.workflow_dir_user / f"{self.file_prefix}.err"
80
- self.log = self.workflow_dir_user / f"{self.file_prefix}.log"
106
+ self.subfolder_name = task_subfolder_name(
107
+ order=order, task_name=self.task_name
108
+ )
109
+ self.remote_subfolder = self.workflow_dir_remote / self.subfolder_name
110
+ self.args = self.remote_subfolder / f"{self.file_prefix}.args.json"
111
+ self.out = self.remote_subfolder / f"{self.file_prefix}.out"
112
+ self.err = self.remote_subfolder / f"{self.file_prefix}.err"
113
+ self.log = self.remote_subfolder / f"{self.file_prefix}.log"
81
114
  self.metadiff = (
82
- self.workflow_dir_user / f"{self.file_prefix}.metadiff.json"
115
+ self.remote_subfolder / f"{self.file_prefix}.metadiff.json"
83
116
  )
84
117
 
85
118
 
86
119
  def get_task_file_paths(
87
- workflow_dir: Path,
88
- workflow_dir_user: Path,
120
+ workflow_dir_local: Path,
121
+ workflow_dir_remote: Path,
122
+ task_name: str,
89
123
  task_order: Optional[int] = None,
90
124
  component: Optional[str] = None,
91
125
  ) -> TaskFiles:
92
126
  """
93
127
  Return the corrisponding TaskFiles object
94
-
95
- This function is mainly used as a cache to avoid instantiating needless
96
- objects.
97
128
  """
98
129
  return TaskFiles(
99
- workflow_dir=workflow_dir,
100
- workflow_dir_user=workflow_dir_user,
130
+ workflow_dir_local=workflow_dir_local,
131
+ workflow_dir_remote=workflow_dir_remote,
132
+ task_name=task_name,
101
133
  task_order=task_order,
102
134
  component=component,
103
135
  )
@@ -33,7 +33,11 @@ from ...models.v1 import WorkflowTask
33
33
  from ...schemas.v1 import JobStatusTypeV1
34
34
  from ..exceptions import JobExecutionError
35
35
  from ..exceptions import TaskExecutionError
36
+ from ..executors.slurm._subprocess_run_as_user import (
37
+ _mkdir_as_user,
38
+ )
36
39
  from ..filenames import WORKFLOW_LOG_FILENAME
40
+ from ..task_files import task_subfolder_name
37
41
  from ._local import process_workflow as local_process_workflow
38
42
  from ._slurm import process_workflow as slurm_process_workflow
39
43
  from .common import close_job_logger
@@ -137,41 +141,48 @@ async def submit_workflow(
137
141
  # Define and create server-side working folder
138
142
  project_id = workflow.project_id
139
143
  timestamp_string = get_timestamp().strftime("%Y%m%d_%H%M%S")
140
- WORKFLOW_DIR = (
141
- settings.FRACTAL_RUNNER_WORKING_BASE_DIR
142
- / (
143
- f"proj_{project_id:07d}_wf_{workflow_id:07d}_job_{job_id:07d}"
144
- f"_{timestamp_string}"
145
- )
146
- ).resolve()
144
+ WORKFLOW_DIR_LOCAL = settings.FRACTAL_RUNNER_WORKING_BASE_DIR / (
145
+ f"proj_{project_id:07d}_wf_{workflow_id:07d}_job_{job_id:07d}"
146
+ f"_{timestamp_string}"
147
+ )
147
148
 
148
- if WORKFLOW_DIR.exists():
149
- raise RuntimeError(f"Workflow dir {WORKFLOW_DIR} already exists.")
149
+ if WORKFLOW_DIR_LOCAL.exists():
150
+ raise RuntimeError(
151
+ f"Workflow dir {WORKFLOW_DIR_LOCAL} already exists."
152
+ )
150
153
 
151
- # Create WORKFLOW_DIR with 755 permissions
154
+ # Create WORKFLOW_DIR
152
155
  original_umask = os.umask(0)
153
- WORKFLOW_DIR.mkdir(parents=True, mode=0o755)
156
+ WORKFLOW_DIR_LOCAL.mkdir(parents=True, mode=0o755)
154
157
  os.umask(original_umask)
155
158
 
156
- # Define and create user-side working folder, if needed
159
+ # Define and create WORKFLOW_DIR_REMOTE
157
160
  if FRACTAL_RUNNER_BACKEND == "local":
158
- WORKFLOW_DIR_USER = WORKFLOW_DIR
161
+ WORKFLOW_DIR_REMOTE = WORKFLOW_DIR_LOCAL
159
162
  elif FRACTAL_RUNNER_BACKEND == "slurm":
160
-
161
- from ..executors.slurm._subprocess_run_as_user import (
162
- _mkdir_as_user,
163
+ WORKFLOW_DIR_REMOTE = (
164
+ Path(user_cache_dir) / WORKFLOW_DIR_LOCAL.name
163
165
  )
166
+ _mkdir_as_user(folder=str(WORKFLOW_DIR_REMOTE), user=slurm_user)
164
167
 
165
- WORKFLOW_DIR_USER = (
166
- Path(user_cache_dir) / f"{WORKFLOW_DIR.name}"
167
- ).resolve()
168
- _mkdir_as_user(folder=str(WORKFLOW_DIR_USER), user=slurm_user)
169
- else:
170
- raise ValueError(f"{FRACTAL_RUNNER_BACKEND=} not supported")
168
+ # Create all tasks subfolders
169
+ for order in range(job.first_task_index, job.last_task_index + 1):
170
+ subfolder_name = task_subfolder_name(
171
+ order=order,
172
+ task_name=workflow.task_list[order].task.name,
173
+ )
174
+ original_umask = os.umask(0)
175
+ (WORKFLOW_DIR_LOCAL / subfolder_name).mkdir(mode=0o755)
176
+ os.umask(original_umask)
177
+ if FRACTAL_RUNNER_BACKEND == "slurm":
178
+ _mkdir_as_user(
179
+ folder=str(WORKFLOW_DIR_REMOTE / subfolder_name),
180
+ user=slurm_user,
181
+ )
171
182
 
172
183
  # Update db
173
- job.working_dir = WORKFLOW_DIR.as_posix()
174
- job.working_dir_user = WORKFLOW_DIR_USER.as_posix()
184
+ job.working_dir = WORKFLOW_DIR_LOCAL.as_posix()
185
+ job.working_dir_user = WORKFLOW_DIR_REMOTE.as_posix()
175
186
  db_sync.merge(job)
176
187
  db_sync.commit()
177
188
 
@@ -192,7 +203,7 @@ async def submit_workflow(
192
203
 
193
204
  # Write logs
194
205
  logger_name = f"WF{workflow_id}_job{job_id}"
195
- log_file_path = WORKFLOW_DIR / WORKFLOW_LOG_FILENAME
206
+ log_file_path = WORKFLOW_DIR_LOCAL / WORKFLOW_LOG_FILENAME
196
207
  logger = set_logger(
197
208
  logger_name=logger_name,
198
209
  log_file_path=log_file_path,
@@ -239,8 +250,8 @@ async def submit_workflow(
239
250
  slurm_user=slurm_user,
240
251
  slurm_account=job.slurm_account,
241
252
  user_cache_dir=user_cache_dir,
242
- workflow_dir=WORKFLOW_DIR,
243
- workflow_dir_user=WORKFLOW_DIR_USER,
253
+ workflow_dir_local=WORKFLOW_DIR_LOCAL,
254
+ workflow_dir_remote=WORKFLOW_DIR_REMOTE,
244
255
  logger_name=logger_name,
245
256
  worker_init=worker_init,
246
257
  first_task_index=job.first_task_index,
@@ -36,8 +36,8 @@ from fractal_server.app.runner.task_files import get_task_file_paths
36
36
  def no_op_submit_setup_call(
37
37
  *,
38
38
  wftask: WorkflowTask,
39
- workflow_dir: Path,
40
- workflow_dir_user: Path,
39
+ workflow_dir_local: Path,
40
+ workflow_dir_remote: Path,
41
41
  ) -> dict:
42
42
  """
43
43
  Default (no-operation) interface of submit_setup_call.
@@ -113,8 +113,8 @@ def call_single_task(
113
113
  *,
114
114
  wftask: WorkflowTask,
115
115
  task_pars: TaskParameters,
116
- workflow_dir: Path,
117
- workflow_dir_user: Optional[Path] = None,
116
+ workflow_dir_local: Path,
117
+ workflow_dir_remote: Optional[Path] = None,
118
118
  logger_name: Optional[str] = None,
119
119
  ) -> TaskParameters:
120
120
  """
@@ -133,8 +133,8 @@ def call_single_task(
133
133
 
134
134
  If the executor then impersonates another user (as in the
135
135
  `FractalSlurmExecutor`), this function is run by that user. For this
136
- reason, it should not write any file to workflow_dir, or it may yield
137
- permission errors.
136
+ reason, it should not write any file to `workflow_dir_local`, or it may
137
+ yield permission errors.
138
138
 
139
139
  Args:
140
140
  wftask:
@@ -143,12 +143,12 @@ def call_single_task(
143
143
  task_pars:
144
144
  The parameters required to run the task which are not specific to
145
145
  the task, e.g., I/O paths.
146
- workflow_dir:
146
+ workflow_dir_local:
147
147
  The server-side working directory for workflow execution.
148
- workflow_dir_user:
148
+ workflow_dir_remote:
149
149
  The user-side working directory for workflow execution (only
150
150
  relevant for multi-user executors). If `None`, it is set to be
151
- equal to `workflow_dir`.
151
+ equal to `workflow_dir_remote`.
152
152
  logger_name:
153
153
  Name of the logger
154
154
 
@@ -164,18 +164,18 @@ def call_single_task(
164
164
  information to the TaskExecutionError, such as task
165
165
  order and name.
166
166
  JobExecutionError: If the wrapped task raises a job-related error.
167
- RuntimeError: If the `workflow_dir` is falsy.
168
167
  """
169
168
 
170
169
  logger = get_logger(logger_name)
171
170
 
172
- if not workflow_dir_user:
173
- workflow_dir_user = workflow_dir
171
+ if not workflow_dir_remote:
172
+ workflow_dir_remote = workflow_dir_local
174
173
 
175
174
  task_files = get_task_file_paths(
176
- workflow_dir=workflow_dir,
177
- workflow_dir_user=workflow_dir_user,
175
+ workflow_dir_local=workflow_dir_local,
176
+ workflow_dir_remote=workflow_dir_remote,
178
177
  task_order=wftask.order,
178
+ task_name=wftask.task.name,
179
179
  )
180
180
 
181
181
  # write args file (by assembling task_pars and wftask.args)
@@ -248,8 +248,8 @@ def call_single_parallel_task(
248
248
  *,
249
249
  wftask: WorkflowTask,
250
250
  task_pars: TaskParameters,
251
- workflow_dir: Path,
252
- workflow_dir_user: Optional[Path] = None,
251
+ workflow_dir_local: Path,
252
+ workflow_dir_remote: Optional[Path] = None,
253
253
  ) -> Any:
254
254
  """
255
255
  Call a single instance of a parallel task
@@ -274,9 +274,9 @@ def call_single_parallel_task(
274
274
  The task to execute.
275
275
  task_pars:
276
276
  The parameters to pass on to the task.
277
- workflow_dir:
277
+ workflow_dir_local:
278
278
  The server-side working directory for workflow execution.
279
- workflow_dir_user:
279
+ workflow_dir_remote:
280
280
  The user-side working directory for workflow execution (only
281
281
  relevant for multi-user executors).
282
282
 
@@ -290,17 +290,18 @@ def call_single_parallel_task(
290
290
  information to the TaskExecutionError, such as task
291
291
  order and name.
292
292
  JobExecutionError: If the wrapped task raises a job-related error.
293
- RuntimeError: If the `workflow_dir` is falsy.
293
+ RuntimeError: If the `workflow_dir_local` is falsy.
294
294
  """
295
- if not workflow_dir:
295
+ if not workflow_dir_local:
296
296
  raise RuntimeError
297
- if not workflow_dir_user:
298
- workflow_dir_user = workflow_dir
297
+ if not workflow_dir_remote:
298
+ workflow_dir_remote = workflow_dir_local
299
299
 
300
300
  task_files = get_task_file_paths(
301
- workflow_dir=workflow_dir,
302
- workflow_dir_user=workflow_dir_user,
301
+ workflow_dir_local=workflow_dir_local,
302
+ workflow_dir_remote=workflow_dir_remote,
303
303
  task_order=wftask.order,
304
+ task_name=wftask.task.name,
304
305
  component=component,
305
306
  )
306
307
 
@@ -363,8 +364,8 @@ def call_parallel_task(
363
364
  executor: Executor,
364
365
  wftask: WorkflowTask,
365
366
  task_pars_depend: TaskParameters,
366
- workflow_dir: Path,
367
- workflow_dir_user: Optional[Path] = None,
367
+ workflow_dir_local: Path,
368
+ workflow_dir_remote: Optional[Path] = None,
368
369
  submit_setup_call: Callable = no_op_submit_setup_call,
369
370
  logger_name: Optional[str] = None,
370
371
  ) -> TaskParameters:
@@ -387,9 +388,9 @@ def call_parallel_task(
387
388
  The parallel task to run.
388
389
  task_pars_depend:
389
390
  The task parameters to be passed on to the parallel task.
390
- workflow_dir:
391
+ workflow_dir_local:
391
392
  The server-side working directory for workflow execution.
392
- workflow_dir_user:
393
+ workflow_dir_remote:
393
394
  The user-side working directory for workflow execution (only
394
395
  relevant for multi-user executors).
395
396
  submit_setup_call:
@@ -405,8 +406,8 @@ def call_parallel_task(
405
406
  """
406
407
  logger = get_logger(logger_name)
407
408
 
408
- if not workflow_dir_user:
409
- workflow_dir_user = workflow_dir
409
+ if not workflow_dir_remote:
410
+ workflow_dir_remote = workflow_dir_local
410
411
 
411
412
  try:
412
413
  component_list = task_pars_depend.metadata[
@@ -424,8 +425,8 @@ def call_parallel_task(
424
425
  try:
425
426
  extra_setup = submit_setup_call(
426
427
  wftask=wftask,
427
- workflow_dir=workflow_dir,
428
- workflow_dir_user=workflow_dir_user,
428
+ workflow_dir_local=workflow_dir_local,
429
+ workflow_dir_remote=workflow_dir_remote,
429
430
  )
430
431
  except Exception as e:
431
432
  tb = "".join(traceback.format_tb(e.__traceback__))
@@ -443,8 +444,8 @@ def call_parallel_task(
443
444
  call_single_parallel_task,
444
445
  wftask=wftask,
445
446
  task_pars=actual_task_pars_depend,
446
- workflow_dir=workflow_dir,
447
- workflow_dir_user=workflow_dir_user,
447
+ workflow_dir_local=workflow_dir_local,
448
+ workflow_dir_remote=workflow_dir_remote,
448
449
  )
449
450
 
450
451
  # Submit tasks for execution. Note that `for _ in map_iter:
@@ -511,16 +512,17 @@ def execute_tasks(
511
512
  executor: Executor,
512
513
  task_list: list[WorkflowTask],
513
514
  task_pars: TaskParameters,
514
- workflow_dir: Path,
515
- workflow_dir_user: Optional[Path] = None,
515
+ workflow_dir_local: Path,
516
+ workflow_dir_remote: Optional[Path] = None,
516
517
  submit_setup_call: Callable = no_op_submit_setup_call,
517
518
  logger_name: str,
518
519
  ) -> TaskParameters:
519
520
  """
520
521
  Submit a list of WorkflowTasks for execution
521
522
 
522
- **Note:** At the end of each task, write current metadata to `working_dir /
523
- METADATA_FILENAME`, so that they can be read as part of the [`get_job`
523
+ **Note:** At the end of each task, write current metadata to
524
+ `workflow_dir_local / METADATA_FILENAME`, so that they can be read as part
525
+ of the [`get_job`
524
526
  endpoint](../../api/v1/job/#fractal_server.app.routes.api.v1.job.get_job).
525
527
 
526
528
  Arguments:
@@ -531,12 +533,12 @@ def execute_tasks(
531
533
  The list of wftasks to be run
532
534
  task_pars:
533
535
  The task parameters to be passed on to the first task of the list.
534
- workflow_dir:
536
+ workflow_dir_local:
535
537
  The server-side working directory for workflow execution.
536
- workflow_dir_user:
538
+ workflow_dir_remote:
537
539
  The user-side working directory for workflow execution (only
538
540
  relevant for multi-user executors). If `None`, it is set to be
539
- equal to `workflow_dir`.
541
+ equal to `workflow_dir_local`.
540
542
  submit_setup_call:
541
543
  An optional function that computes configuration parameters for
542
544
  the executor.
@@ -548,8 +550,8 @@ def execute_tasks(
548
550
  A TaskParameters object which constitutes the output of the last
549
551
  task in the list.
550
552
  """
551
- if not workflow_dir_user:
552
- workflow_dir_user = workflow_dir
553
+ if not workflow_dir_remote:
554
+ workflow_dir_remote = workflow_dir_local
553
555
 
554
556
  logger = get_logger(logger_name)
555
557
 
@@ -565,8 +567,8 @@ def execute_tasks(
565
567
  executor=executor,
566
568
  wftask=this_wftask,
567
569
  task_pars_depend=current_task_pars,
568
- workflow_dir=workflow_dir,
569
- workflow_dir_user=workflow_dir_user,
570
+ workflow_dir_local=workflow_dir_local,
571
+ workflow_dir_remote=workflow_dir_remote,
570
572
  submit_setup_call=submit_setup_call,
571
573
  logger_name=logger_name,
572
574
  )
@@ -575,8 +577,8 @@ def execute_tasks(
575
577
  try:
576
578
  extra_setup = submit_setup_call(
577
579
  wftask=this_wftask,
578
- workflow_dir=workflow_dir,
579
- workflow_dir_user=workflow_dir_user,
580
+ workflow_dir_local=workflow_dir_local,
581
+ workflow_dir_remote=workflow_dir_remote,
580
582
  )
581
583
  except Exception as e:
582
584
  tb = "".join(traceback.format_tb(e.__traceback__))
@@ -594,8 +596,8 @@ def execute_tasks(
594
596
  call_single_task,
595
597
  wftask=this_wftask,
596
598
  task_pars=current_task_pars,
597
- workflow_dir=workflow_dir,
598
- workflow_dir_user=workflow_dir_user,
599
+ workflow_dir_local=workflow_dir_local,
600
+ workflow_dir_remote=workflow_dir_remote,
599
601
  logger_name=logger_name,
600
602
  **extra_setup,
601
603
  )
@@ -607,11 +609,11 @@ def execute_tasks(
607
609
  )
608
610
 
609
611
  # Write most recent metadata to METADATA_FILENAME
610
- with open(workflow_dir / METADATA_FILENAME, "w") as f:
612
+ with open(workflow_dir_local / METADATA_FILENAME, "w") as f:
611
613
  json.dump(current_task_pars.metadata, f, indent=2)
612
614
 
613
615
  # Write most recent metadata to HISTORY_FILENAME
614
- with open(workflow_dir / HISTORY_FILENAME, "w") as f:
616
+ with open(workflow_dir_local / HISTORY_FILENAME, "w") as f:
615
617
  json.dump(current_task_pars.history, f, indent=2)
616
618
 
617
619
  return current_task_pars
@@ -40,7 +40,7 @@ def _process_workflow(
40
40
  input_metadata: dict[str, Any],
41
41
  input_history: list[dict[str, Any]],
42
42
  logger_name: str,
43
- workflow_dir: Path,
43
+ workflow_dir_local: Path,
44
44
  first_task_index: int,
45
45
  last_task_index: int,
46
46
  ) -> dict[str, Any]:
@@ -66,8 +66,8 @@ def _process_workflow(
66
66
  metadata=input_metadata,
67
67
  history=input_history,
68
68
  ),
69
- workflow_dir=workflow_dir,
70
- workflow_dir_user=workflow_dir,
69
+ workflow_dir_local=workflow_dir_local,
70
+ workflow_dir_remote=workflow_dir_local,
71
71
  logger_name=logger_name,
72
72
  submit_setup_call=_local_submit_setup,
73
73
  )
@@ -85,8 +85,8 @@ async def process_workflow(
85
85
  input_metadata: dict[str, Any],
86
86
  input_history: list[dict[str, Any]],
87
87
  logger_name: str,
88
- workflow_dir: Path,
89
- workflow_dir_user: Optional[Path] = None,
88
+ workflow_dir_local: Path,
89
+ workflow_dir_remote: Optional[Path] = None,
90
90
  slurm_user: Optional[str] = None,
91
91
  slurm_account: Optional[str] = None,
92
92
  user_cache_dir: Optional[str] = None,
@@ -115,12 +115,13 @@ async def process_workflow(
115
115
  Initial metadata, passed to the first task
116
116
  logger_name:
117
117
  Name of the logger to log information on the run to
118
- workflow_dir:
118
+ workflow_dir_local:
119
119
  Working directory for this run.
120
- workflow_dir_user:
120
+ workflow_dir_remote:
121
121
  Working directory for this run, on the user side. This argument is
122
122
  present for compatibility with the standard backend interface, but
123
- for the `local` backend it cannot be different from `workflow_dir`.
123
+ for the `local` backend it cannot be different from
124
+ `workflow_dir_local`.
124
125
  slurm_user:
125
126
  Username to impersonate to run the workflow. This argument is
126
127
  present for compatibility with the standard backend interface, but
@@ -157,10 +158,10 @@ async def process_workflow(
157
158
  of the workflow
158
159
  """
159
160
 
160
- if workflow_dir_user and (workflow_dir_user != workflow_dir):
161
+ if workflow_dir_remote and (workflow_dir_remote != workflow_dir_local):
161
162
  raise NotImplementedError(
162
163
  "Local backend does not support different directories "
163
- f"{workflow_dir=} and {workflow_dir_user=}"
164
+ f"{workflow_dir_local=} and {workflow_dir_remote=}"
164
165
  )
165
166
 
166
167
  # Set values of first_task_index and last_task_index
@@ -178,7 +179,7 @@ async def process_workflow(
178
179
  input_metadata=input_metadata,
179
180
  input_history=input_history,
180
181
  logger_name=logger_name,
181
- workflow_dir=workflow_dir,
182
+ workflow_dir_local=workflow_dir_local,
182
183
  first_task_index=first_task_index,
183
184
  last_task_index=last_task_index,
184
185
  )
@@ -21,8 +21,8 @@ from ._local_config import get_local_backend_config
21
21
  def _local_submit_setup(
22
22
  *,
23
23
  wftask: WorkflowTask,
24
- workflow_dir: Optional[Path] = None,
25
- workflow_dir_user: Optional[Path] = None,
24
+ workflow_dir_local: Optional[Path] = None,
25
+ workflow_dir_remote: Optional[Path] = None,
26
26
  ) -> dict[str, object]:
27
27
  """
28
28
  Collect WorfklowTask-specific configuration parameters from different
@@ -31,9 +31,9 @@ def _local_submit_setup(
31
31
  Arguments:
32
32
  wftask:
33
33
  WorkflowTask for which the configuration is to be assembled
34
- workflow_dir:
34
+ workflow_dir_local:
35
35
  Not used in this function.
36
- workflow_dir_user:
36
+ workflow_dir_remote:
37
37
  Not used in this function.
38
38
 
39
39
  Returns: