fractal-server 2.0.0a11__py3-none-any.whl → 2.0.0a12__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 (38) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/__init__.py +5 -2
  3. fractal_server/app/models/v1/state.py +1 -1
  4. fractal_server/app/models/v2/workflowtask.py +0 -39
  5. fractal_server/app/routes/admin/v1.py +5 -5
  6. fractal_server/app/routes/api/v1/_aux_functions.py +6 -6
  7. fractal_server/app/routes/api/v1/dataset.py +4 -4
  8. fractal_server/app/routes/api/v1/project.py +4 -4
  9. fractal_server/app/routes/api/v1/task.py +2 -2
  10. fractal_server/app/routes/api/v1/task_collection.py +3 -3
  11. fractal_server/app/routes/api/v1/workflow.py +4 -4
  12. fractal_server/app/routes/api/v1/workflowtask.py +1 -1
  13. fractal_server/app/routes/api/v2/dataset.py +10 -0
  14. fractal_server/app/routes/api/v2/job.py +1 -1
  15. fractal_server/app/routes/api/v2/project.py +5 -17
  16. fractal_server/app/routes/api/v2/submit.py +1 -1
  17. fractal_server/app/routes/api/v2/task_collection.py +2 -2
  18. fractal_server/app/routes/aux/_job.py +1 -1
  19. fractal_server/app/runner/task_files.py +1 -1
  20. fractal_server/app/runner/v1/__init__.py +4 -4
  21. fractal_server/app/runner/v1/_common.py +2 -2
  22. fractal_server/app/runner/v1/_local/__init__.py +3 -3
  23. fractal_server/app/runner/v2/runner.py +62 -25
  24. fractal_server/app/runner/v2/runner_functions.py +36 -10
  25. fractal_server/app/runner/v2/runner_functions_low_level.py +6 -13
  26. fractal_server/app/runner/v2/task_interface.py +4 -2
  27. fractal_server/app/schemas/__init__.py +0 -3
  28. fractal_server/app/schemas/v1/__init__.py +0 -6
  29. fractal_server/app/schemas/v2/__init__.py +2 -0
  30. fractal_server/app/schemas/v2/dataset.py +5 -0
  31. fractal_server/app/schemas/v2/workflowtask.py +60 -14
  32. fractal_server/images/tools.py +0 -1
  33. fractal_server/tasks/v1/background_operations.py +2 -2
  34. {fractal_server-2.0.0a11.dist-info → fractal_server-2.0.0a12.dist-info}/METADATA +1 -1
  35. {fractal_server-2.0.0a11.dist-info → fractal_server-2.0.0a12.dist-info}/RECORD +38 -38
  36. {fractal_server-2.0.0a11.dist-info → fractal_server-2.0.0a12.dist-info}/LICENSE +0 -0
  37. {fractal_server-2.0.0a11.dist-info → fractal_server-2.0.0a12.dist-info}/WHEEL +0 -0
  38. {fractal_server-2.0.0a11.dist-info → fractal_server-2.0.0a12.dist-info}/entry_points.txt +0 -0
@@ -1 +1 @@
1
- __VERSION__ = "2.0.0a11"
1
+ __VERSION__ = "2.0.0a12"
@@ -2,5 +2,8 @@
2
2
  `models` module
3
3
  """
4
4
  from .security import * # noqa: F401, F403
5
- from .v1 import * # noqa: F401, F403
6
- from .v2 import * # noqa: F401, F403
5
+ from .v1 import Project # noqa: F401
6
+ from .v2 import ProjectV2 # noqa: F401
7
+
8
+ # We include the project models to avoid issues with LinkUserProject
9
+ # (sometimes taking place in alembic autogenerate)
@@ -9,7 +9,7 @@ from sqlmodel import Field
9
9
  from sqlmodel import SQLModel
10
10
 
11
11
  from ....utils import get_timestamp
12
- from ...schemas.v1 import _StateBase
12
+ from ...schemas.state import _StateBase
13
13
 
14
14
 
15
15
  class State(_StateBase, SQLModel, table=True):
@@ -2,7 +2,6 @@ from typing import Any
2
2
  from typing import Literal
3
3
  from typing import Optional
4
4
 
5
- from pydantic import validator
6
5
  from sqlalchemy import Column
7
6
  from sqlalchemy.types import JSON
8
7
  from sqlmodel import Field
@@ -48,41 +47,3 @@ class WorkflowTaskV2(SQLModel, table=True):
48
47
  task_legacy: Optional[Task] = Relationship(
49
48
  sa_relationship_kwargs=dict(lazy="selectin")
50
49
  )
51
-
52
- @validator("args_non_parallel")
53
- def validate_args_non_parallel(cls, value):
54
- if value is None:
55
- return
56
- forbidden_args_keys = {
57
- "zarr_dir",
58
- "zarr_url",
59
- "zarr_urls",
60
- "init_args",
61
- }
62
- args_keys = set(value.keys())
63
- intersect_keys = forbidden_args_keys.intersection(args_keys)
64
- if intersect_keys:
65
- raise ValueError(
66
- "`args` contains the following forbidden keys: "
67
- f"{intersect_keys}"
68
- )
69
- return value
70
-
71
- @validator("args_parallel")
72
- def validate_args_parallel(cls, value):
73
- if value is None:
74
- return
75
- forbidden_args_keys = {
76
- "zarr_dir",
77
- "zarr_url",
78
- "zarr_urls",
79
- "init_args",
80
- }
81
- args_keys = set(value.keys())
82
- intersect_keys = forbidden_args_keys.intersection(args_keys)
83
- if intersect_keys:
84
- raise ValueError(
85
- "`args` contains the following forbidden keys: "
86
- f"{intersect_keys}"
87
- )
88
- return value
@@ -20,12 +20,12 @@ from ....syringe import Inject
20
20
  from ....utils import get_timestamp
21
21
  from ...db import AsyncSession
22
22
  from ...db import get_async_db
23
- from ...models import ApplyWorkflow
24
- from ...models import Dataset
25
- from ...models import JobStatusTypeV1
26
- from ...models import Project
27
- from ...models import Workflow
28
23
  from ...models.security import UserOAuth as User
24
+ from ...models.v1 import ApplyWorkflow
25
+ from ...models.v1 import Dataset
26
+ from ...models.v1 import JobStatusTypeV1
27
+ from ...models.v1 import Project
28
+ from ...models.v1 import Workflow
29
29
  from ...runner.filenames import WORKFLOW_LOG_FILENAME
30
30
  from ...schemas.v1 import ApplyWorkflowReadV1
31
31
  from ...schemas.v1 import ApplyWorkflowUpdateV1
@@ -12,13 +12,13 @@ from sqlmodel import select
12
12
  from sqlmodel.sql.expression import SelectOfScalar
13
13
 
14
14
  from ....db import AsyncSession
15
- from ....models import ApplyWorkflow
16
- from ....models import Dataset
17
15
  from ....models import LinkUserProject
18
- from ....models import Project
19
- from ....models import Task
20
- from ....models import Workflow
21
- from ....models import WorkflowTask
16
+ from ....models.v1 import ApplyWorkflow
17
+ from ....models.v1 import Dataset
18
+ from ....models.v1 import Project
19
+ from ....models.v1 import Task
20
+ from ....models.v1 import Workflow
21
+ from ....models.v1 import WorkflowTask
22
22
  from ....schemas.v1 import JobStatusTypeV1
23
23
  from ....security import User
24
24
 
@@ -13,10 +13,10 @@ from sqlmodel import select
13
13
 
14
14
  from ....db import AsyncSession
15
15
  from ....db import get_async_db
16
- from ....models import ApplyWorkflow
17
- from ....models import Dataset
18
- from ....models import Project
19
- from ....models import Resource
16
+ from ....models.v1 import ApplyWorkflow
17
+ from ....models.v1 import Dataset
18
+ from ....models.v1 import Project
19
+ from ....models.v1 import Resource
20
20
  from ....runner.filenames import HISTORY_FILENAME
21
21
  from ....schemas.v1 import DatasetCreateV1
22
22
  from ....schemas.v1 import DatasetReadV1
@@ -18,11 +18,11 @@ from .....logger import set_logger
18
18
  from .....syringe import Inject
19
19
  from ....db import AsyncSession
20
20
  from ....db import get_async_db
21
- from ....models import ApplyWorkflow
22
- from ....models import Dataset
23
21
  from ....models import LinkUserProject
24
- from ....models import Project
25
- from ....models import Workflow
22
+ from ....models.v1 import ApplyWorkflow
23
+ from ....models.v1 import Dataset
24
+ from ....models.v1 import Project
25
+ from ....models.v1 import Workflow
26
26
  from ....runner.set_start_and_last_task_index import (
27
27
  set_start_and_last_task_index,
28
28
  )
@@ -11,8 +11,8 @@ from sqlmodel import select
11
11
  from .....logger import set_logger
12
12
  from ....db import AsyncSession
13
13
  from ....db import get_async_db
14
- from ....models import Task
15
- from ....models import WorkflowTask
14
+ from ....models.v1 import Task
15
+ from ....models.v1 import WorkflowTask
16
16
  from ....models.v2 import TaskV2
17
17
  from ....schemas.v1 import TaskCreateV1
18
18
  from ....schemas.v1 import TaskReadV1
@@ -17,9 +17,9 @@ from .....logger import set_logger
17
17
  from .....syringe import Inject
18
18
  from ....db import AsyncSession
19
19
  from ....db import get_async_db
20
- from ....models import State
21
- from ....models import Task
22
- from ....schemas.v1 import StateRead
20
+ from ....models.v1 import State
21
+ from ....models.v1 import Task
22
+ from ....schemas.state import StateRead
23
23
  from ....schemas.v1 import TaskCollectPipV1
24
24
  from ....schemas.v1 import TaskCollectStatusV1
25
25
  from ....security import current_active_user
@@ -23,10 +23,10 @@ from .....logger import close_logger
23
23
  from .....logger import set_logger
24
24
  from ....db import AsyncSession
25
25
  from ....db import get_async_db
26
- from ....models import ApplyWorkflow
27
- from ....models import Project
28
- from ....models import Task
29
- from ....models import Workflow
26
+ from ....models.v1 import ApplyWorkflow
27
+ from ....models.v1 import Project
28
+ from ....models.v1 import Task
29
+ from ....models.v1 import Workflow
30
30
  from ....schemas.v1 import WorkflowCreateV1
31
31
  from ....schemas.v1 import WorkflowExportV1
32
32
  from ....schemas.v1 import WorkflowImportV1
@@ -22,7 +22,7 @@ from fastapi import status
22
22
 
23
23
  from ....db import AsyncSession
24
24
  from ....db import get_async_db
25
- from ....models import Task
25
+ from ....models.v1 import Task
26
26
  from ....schemas.v1 import WorkflowTaskCreateV1
27
27
  from ....schemas.v1 import WorkflowTaskReadV1
28
28
  from ....schemas.v1 import WorkflowTaskUpdateV1
@@ -270,6 +270,16 @@ async def import_dataset(
270
270
  db=db,
271
271
  )
272
272
 
273
+ for image in dataset.images:
274
+ if not image.zarr_url.startswith(dataset.zarr_dir):
275
+ raise HTTPException(
276
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
277
+ detail=(
278
+ f"Cannot import dataset: zarr_url {image.zarr_url} is not "
279
+ f"relative to zarr_dir={dataset.zarr_dir}."
280
+ ),
281
+ )
282
+
273
283
  # Create new Dataset
274
284
  db_dataset = DatasetV2(
275
285
  project_id=project_id,
@@ -12,7 +12,7 @@ from ....db import AsyncSession
12
12
  from ....db import get_async_db
13
13
  from ....models.v2 import JobV2
14
14
  from ....models.v2 import ProjectV2
15
- from ....runner.filenames import WORKFLOW_LOG_FILENAME # FIXME
15
+ from ....runner.filenames import WORKFLOW_LOG_FILENAME
16
16
  from ....schemas.v2 import JobReadV2
17
17
  from ....schemas.v2 import JobStatusTypeV2
18
18
  from ....security import current_active_user
@@ -5,11 +5,8 @@ from fastapi import Depends
5
5
  from fastapi import HTTPException
6
6
  from fastapi import Response
7
7
  from fastapi import status
8
- from sqlalchemy.exc import IntegrityError
9
8
  from sqlmodel import select
10
9
 
11
- from .....logger import close_logger
12
- from .....logger import set_logger
13
10
  from ....db import AsyncSession
14
11
  from ....db import get_async_db
15
12
  from ....models.v2 import DatasetV2
@@ -65,20 +62,11 @@ async def create_project(
65
62
 
66
63
  db_project = ProjectV2(**project.dict())
67
64
  db_project.user_list.append(user)
68
- try:
69
- db.add(db_project)
70
- await db.commit()
71
- await db.refresh(db_project)
72
- await db.close()
73
- except IntegrityError as e:
74
- await db.rollback()
75
- logger = set_logger("create_project")
76
- logger.error(str(e))
77
- close_logger(logger)
78
- raise HTTPException(
79
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
80
- detail=str(e),
81
- )
65
+
66
+ db.add(db_project)
67
+ await db.commit()
68
+ await db.refresh(db_project)
69
+ await db.close()
82
70
 
83
71
  return db_project
84
72
 
@@ -17,7 +17,7 @@ from ....db import get_async_db
17
17
  from ....models.v2 import JobV2
18
18
  from ....runner.set_start_and_last_task_index import (
19
19
  set_start_and_last_task_index,
20
- ) # FIXME V2
20
+ )
21
21
  from ....runner.v2 import submit_workflow
22
22
  from ....schemas.v2 import JobCreateV2
23
23
  from ....schemas.v2 import JobReadV2
@@ -17,9 +17,9 @@ from .....logger import set_logger
17
17
  from .....syringe import Inject
18
18
  from ....db import AsyncSession
19
19
  from ....db import get_async_db
20
- from ....models import CollectionStateV2
20
+ from ....models.v2 import CollectionStateV2
21
21
  from ....models.v2 import TaskV2
22
- from ....schemas import StateRead
22
+ from ....schemas.state import StateRead
23
23
  from ....schemas.v2 import TaskCollectPipV2
24
24
  from ....schemas.v2 import TaskCollectStatusV2
25
25
  from ....security import current_active_user
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from zipfile import ZIP_DEFLATED
4
4
  from zipfile import ZipFile
5
5
 
6
- from ...models import ApplyWorkflow
6
+ from ...models.v1 import ApplyWorkflow
7
7
  from ...runner.filenames import SHUTDOWN_FILENAME
8
8
 
9
9
 
@@ -42,7 +42,7 @@ class TaskFiles:
42
42
  workflow_dir: Path
43
43
  workflow_dir_user: Path
44
44
  task_order: Optional[int] = None
45
- component: Optional[str] = None # FIXME: this is actually for V1 only
45
+ component: Optional[str] = None
46
46
 
47
47
  file_prefix: str
48
48
  args: Path
@@ -26,10 +26,10 @@ from ....logger import set_logger
26
26
  from ....syringe import Inject
27
27
  from ....utils import get_timestamp
28
28
  from ...db import DB
29
- from ...models import ApplyWorkflow
30
- from ...models import Dataset
31
- from ...models import Workflow
32
- from ...models import WorkflowTask
29
+ from ...models.v1 import ApplyWorkflow
30
+ from ...models.v1 import Dataset
31
+ from ...models.v1 import Workflow
32
+ from ...models.v1 import WorkflowTask
33
33
  from ...schemas.v1 import JobStatusTypeV1
34
34
  from ..exceptions import JobExecutionError
35
35
  from ..exceptions import TaskExecutionError
@@ -21,8 +21,8 @@ from typing import Optional
21
21
  from ....config import get_settings
22
22
  from ....logger import get_logger
23
23
  from ....syringe import Inject
24
- from ...models import Task
25
- from ...models import WorkflowTask
24
+ from ...models.v1 import Task
25
+ from ...models.v1 import WorkflowTask
26
26
  from ...schemas.v1 import WorkflowTaskStatusTypeV1
27
27
  from ..exceptions import JobExecutionError
28
28
  from ..exceptions import TaskExecutionError
@@ -23,11 +23,11 @@ from pathlib import Path
23
23
  from typing import Any
24
24
  from typing import Optional
25
25
 
26
- from ....models import Workflow # FIXME: this is v1 specific
26
+ from ....models.v1 import Workflow
27
27
  from ...async_wrap import async_wrap
28
28
  from ...set_start_and_last_task_index import set_start_and_last_task_index
29
- from .._common import execute_tasks # FIXME: this is v1 specific
30
- from ..common import TaskParameters # FIXME: this is v1 specific
29
+ from .._common import execute_tasks
30
+ from ..common import TaskParameters
31
31
  from ._submit_setup import _local_submit_setup
32
32
  from .executor import FractalThreadPoolExecutor
33
33
 
@@ -12,6 +12,7 @@ from ....images import SingleImage
12
12
  from ....images.tools import filter_image_list
13
13
  from ....images.tools import find_image_by_zarr_url
14
14
  from ....images.tools import match_filter
15
+ from ..exceptions import JobExecutionError
15
16
  from ..filenames import FILTERS_FILENAME
16
17
  from ..filenames import HISTORY_FILENAME
17
18
  from ..filenames import IMAGES_FILENAME
@@ -20,13 +21,12 @@ from .runner_functions import run_v1_task_parallel
20
21
  from .runner_functions import run_v2_task_compound
21
22
  from .runner_functions import run_v2_task_non_parallel
22
23
  from .runner_functions import run_v2_task_parallel
24
+ from .task_interface import TaskOutput
23
25
  from fractal_server.app.models.v2 import DatasetV2
24
26
  from fractal_server.app.models.v2 import WorkflowTaskV2
25
27
  from fractal_server.app.schemas.v2.dataset import _DatasetHistoryItemV2
26
28
  from fractal_server.app.schemas.v2.workflowtask import WorkflowTaskStatusTypeV2
27
29
 
28
- # FIXME: define RESERVED_ARGUMENTS = [", ...]
29
-
30
30
 
31
31
  def execute_tasks_v2(
32
32
  wf_task_list: list[WorkflowTaskV2],
@@ -52,8 +52,15 @@ def execute_tasks_v2(
52
52
  for wftask in wf_task_list:
53
53
  task = wftask.task
54
54
  task_legacy = wftask.task_legacy
55
- task_name = task_legacy.name if wftask.is_legacy_task else task.name
56
- logger.debug(f'SUBMIT {wftask.order}-th task (name="{task_name}")')
55
+ if wftask.is_legacy_task:
56
+ task_name = task_legacy.name
57
+ logger.debug(
58
+ f"SUBMIT {wftask.order}-th task "
59
+ f'(legacy, name="{task_name}")'
60
+ )
61
+ else:
62
+ task_name = task.name
63
+ logger.debug(f'SUBMIT {wftask.order}-th task (name="{task_name}")')
57
64
 
58
65
  # PRE TASK EXECUTION
59
66
 
@@ -72,9 +79,11 @@ def execute_tasks_v2(
72
79
  if not wftask.is_legacy_task:
73
80
  for image in filtered_images:
74
81
  if not match_filter(image, Filters(types=task.input_types)):
75
- raise ValueError(
76
- f"Filtered images include {image}, which does "
77
- f"not comply with {task.input_types=}."
82
+ raise JobExecutionError(
83
+ "Invalid filtered image list\n"
84
+ f"Task input types: {task.input_types=}\n"
85
+ f'Image zarr_url: {image["zarr_url"]}\n'
86
+ f'Image types: {image["types"]}\n'
78
87
  )
79
88
 
80
89
  # TASK EXECUTION (V2)
@@ -115,7 +124,7 @@ def execute_tasks_v2(
115
124
  submit_setup_call=submit_setup_call,
116
125
  )
117
126
  else:
118
- raise ValueError(f"Invalid {task.type=}.")
127
+ raise ValueError(f"Unexpected error: Invalid {task.type=}.")
119
128
  # TASK EXECUTION (V1)
120
129
  else:
121
130
  current_task_output = run_v1_task_parallel(
@@ -131,21 +140,36 @@ def execute_tasks_v2(
131
140
 
132
141
  # POST TASK EXECUTION
133
142
 
143
+ # If `current_task_output` includes no images (to be created, edited or
144
+ # removed), then flag all the input images as modified. See
145
+ # fractal-server issue #1374.
146
+ if (
147
+ current_task_output.image_list_updates == []
148
+ and current_task_output.image_list_removals == []
149
+ ):
150
+ current_task_output = TaskOutput(
151
+ **current_task_output.dict(exclude={"image_list_updates"}),
152
+ image_list_updates=[
153
+ dict(zarr_url=img["zarr_url"]) for img in filtered_images
154
+ ],
155
+ )
156
+
134
157
  # Update image list
135
158
  current_task_output.check_zarr_urls_are_unique()
136
159
  for image_obj in current_task_output.image_list_updates:
137
160
  image = image_obj.dict()
138
161
  # Edit existing image
139
- if image["zarr_url"] in [
140
- _image["zarr_url"] for _image in tmp_images
141
- ]:
162
+ tmp_image_paths = [img["zarr_url"] for img in tmp_images]
163
+ if image["zarr_url"] in tmp_image_paths:
142
164
  if (
143
165
  image["origin"] is not None
144
166
  and image["origin"] != image["zarr_url"]
145
167
  ):
146
- raise ValueError(
147
- f"Trying to edit an image with {image['zarr_url']=} "
148
- f"and {image['origin']=}."
168
+ raise JobExecutionError(
169
+ "Cannot edit an image with zarr_url different from "
170
+ "origin.\n"
171
+ f"zarr_url={image['zarr_url']}\n"
172
+ f"origin={image['origin']}"
149
173
  )
150
174
  img_search = find_image_by_zarr_url(
151
175
  images=tmp_images,
@@ -153,6 +177,7 @@ def execute_tasks_v2(
153
177
  )
154
178
  if img_search is None:
155
179
  raise ValueError(
180
+ "Unexpected error: "
156
181
  f"Image with zarr_url {image['zarr_url']} not found, "
157
182
  "while updating image list."
158
183
  )
@@ -188,14 +213,19 @@ def execute_tasks_v2(
188
213
  else:
189
214
  # Check that image['zarr_url'] is relative to zarr_dir
190
215
  if not image["zarr_url"].startswith(zarr_dir):
191
- raise ValueError(
192
- f"{zarr_dir} is not a parent directory of "
193
- f"{image['zarr_url']}"
216
+ raise JobExecutionError(
217
+ "Cannot create image if zarr_dir is not a parent "
218
+ "directory of zarr_url.\n"
219
+ f"zarr_dir: {zarr_dir}\n"
220
+ f"zarr_url: {image['zarr_url']}"
194
221
  )
195
222
  # Check that image['zarr_url'] is not equal to zarr_dir
196
223
  if image["zarr_url"] == zarr_dir:
197
- raise ValueError(
198
- "image['zarr_url'] cannot be equal to zarr_dir"
224
+ raise JobExecutionError(
225
+ "Cannot create image if zarr_url is equal to "
226
+ "zarr_dir.\n"
227
+ f"zarr_dir: {zarr_dir}\n"
228
+ f"zarr_url: {image['zarr_url']}"
199
229
  )
200
230
  # Propagate attributes and types from `origin` (if any)
201
231
  updated_attributes = {}
@@ -236,8 +266,8 @@ def execute_tasks_v2(
236
266
  images=tmp_images, zarr_url=img_zarr_url
237
267
  )
238
268
  if img_search is None:
239
- raise ValueError(
240
- f"Cannot remove missing image with zarr_url {img_zarr_url}"
269
+ raise JobExecutionError(
270
+ f"Cannot remove missing image (zarr_url={img_zarr_url})."
241
271
  )
242
272
  else:
243
273
  tmp_images.pop(img_search["index"])
@@ -249,24 +279,31 @@ def execute_tasks_v2(
249
279
  current_task_output.filters.attributes
250
280
  )
251
281
 
252
- # Update filters.types: current + (task_output + task_manifest)
282
+ # Find manifest ouptut types
253
283
  if wftask.is_legacy_task:
254
284
  types_from_manifest = {}
255
285
  else:
256
286
  types_from_manifest = task.output_types
287
+
288
+ # Find task-output types
257
289
  if current_task_output.filters is not None:
258
290
  types_from_task = current_task_output.filters.types
259
291
  else:
260
292
  types_from_task = {}
293
+
261
294
  # Check that key sets are disjoint
262
295
  set_types_from_manifest = set(types_from_manifest.keys())
263
296
  set_types_from_task = set(types_from_task.keys())
264
297
  if not set_types_from_manifest.isdisjoint(set_types_from_task):
265
298
  overlap = set_types_from_manifest.intersection(set_types_from_task)
266
- raise ValueError(
267
- "Both task and task manifest did set the same"
268
- f"output type. Overlapping keys: {overlap}."
299
+ raise JobExecutionError(
300
+ "Some type filters are being set twice, "
301
+ f"for task '{task_name}'.\n"
302
+ f"Types from task output: {types_from_task}\n"
303
+ f"Types from task maniest: {types_from_manifest}\n"
304
+ f"Overlapping keys: {overlap}"
269
305
  )
306
+
270
307
  # Update filters.types
271
308
  tmp_filters["types"].update(types_from_manifest)
272
309
  tmp_filters["types"].update(types_from_task)
@@ -8,6 +8,9 @@ from typing import Callable
8
8
  from typing import Literal
9
9
  from typing import Optional
10
10
 
11
+ from pydantic import ValidationError
12
+
13
+ from ..exceptions import JobExecutionError
11
14
  from .deduplicate_list import deduplicate_list
12
15
  from .merge_outputs import merge_outputs
13
16
  from .runner_functions_low_level import run_single_task
@@ -31,6 +34,34 @@ __all__ = [
31
34
  MAX_PARALLELIZATION_LIST_SIZE = 20_000
32
35
 
33
36
 
37
+ def _cast_and_validate_TaskOutput(
38
+ task_output: dict[str, Any]
39
+ ) -> Optional[TaskOutput]:
40
+ try:
41
+ validated_task_output = TaskOutput(**task_output)
42
+ return validated_task_output
43
+ except ValidationError as e:
44
+ raise JobExecutionError(
45
+ "Validation of task output failed.\n"
46
+ f"Original error: {str(e)}\n"
47
+ f"Original data: {task_output}."
48
+ )
49
+
50
+
51
+ def _cast_and_validate_InitTaskOutput(
52
+ init_task_output: dict[str, Any],
53
+ ) -> Optional[InitTaskOutput]:
54
+ try:
55
+ validated_init_task_output = InitTaskOutput(**init_task_output)
56
+ return validated_init_task_output
57
+ except ValidationError as e:
58
+ raise JobExecutionError(
59
+ "Validation of init-task output failed.\n"
60
+ f"Original error: {str(e)}\n"
61
+ f"Original data: {init_task_output}."
62
+ )
63
+
64
+
34
65
  def no_op_submit_setup_call(
35
66
  *,
36
67
  wftask: WorkflowTaskV2,
@@ -71,7 +102,7 @@ def _get_executor_options(
71
102
 
72
103
  def _check_parallelization_list_size(my_list):
73
104
  if len(my_list) > MAX_PARALLELIZATION_LIST_SIZE:
74
- raise ValueError(
105
+ raise JobExecutionError(
75
106
  "Too many parallelization items.\n"
76
107
  f" {len(my_list)}\n"
77
108
  f" {MAX_PARALLELIZATION_LIST_SIZE=}\n"
@@ -126,12 +157,10 @@ def run_v2_task_non_parallel(
126
157
  **executor_options,
127
158
  )
128
159
  output = future.result()
129
- # FIXME V2: handle validation errors
130
160
  if output is None:
131
161
  return TaskOutput()
132
162
  else:
133
- validated_output = TaskOutput(**output)
134
- return validated_output
163
+ return _cast_and_validate_TaskOutput(output)
135
164
 
136
165
 
137
166
  def run_v2_task_parallel(
@@ -188,9 +217,7 @@ def run_v2_task_parallel(
188
217
  if output is None:
189
218
  outputs[ind] = TaskOutput()
190
219
  else:
191
- # FIXME: improve handling of validation errors
192
- validated_output = TaskOutput(**output)
193
- outputs[ind] = validated_output
220
+ outputs[ind] = _cast_and_validate_TaskOutput(output)
194
221
 
195
222
  merged_output = merge_outputs(outputs)
196
223
  return merged_output
@@ -245,7 +272,7 @@ def run_v2_task_compound(
245
272
  if output is None:
246
273
  init_task_output = InitTaskOutput()
247
274
  else:
248
- init_task_output = InitTaskOutput(**output)
275
+ init_task_output = _cast_and_validate_InitTaskOutput(output)
249
276
  parallelization_list = init_task_output.parallelization_list
250
277
  parallelization_list = deduplicate_list(parallelization_list)
251
278
 
@@ -285,8 +312,7 @@ def run_v2_task_compound(
285
312
  if output is None:
286
313
  outputs[ind] = TaskOutput()
287
314
  else:
288
- # FIXME: improve handling of validation errors
289
- validated_output = TaskOutput(**output)
315
+ validated_output = _cast_and_validate_TaskOutput(output)
290
316
  outputs[ind] = validated_output
291
317
 
292
318
  merged_output = merge_outputs(outputs)
@@ -76,19 +76,12 @@ def run_single_task(
76
76
  workflow_dir_user = workflow_dir
77
77
 
78
78
  component = args.pop(_COMPONENT_KEY_, None)
79
- if component is None:
80
- task_files = get_task_file_paths(
81
- workflow_dir=workflow_dir,
82
- workflow_dir_user=workflow_dir_user,
83
- task_order=wftask.order,
84
- )
85
- else:
86
- task_files = get_task_file_paths(
87
- workflow_dir=workflow_dir,
88
- workflow_dir_user=workflow_dir_user,
89
- task_order=wftask.order,
90
- component=component,
91
- )
79
+ task_files = get_task_file_paths(
80
+ workflow_dir=workflow_dir,
81
+ workflow_dir_user=workflow_dir_user,
82
+ task_order=wftask.order,
83
+ component=component,
84
+ )
92
85
 
93
86
  # Write arguments to args.json file
94
87
  with task_files.args.open("w") as f:
@@ -29,8 +29,10 @@ class TaskOutput(BaseModel):
29
29
  if zarr_urls.count(zarr_url) > 1
30
30
  ]
31
31
  msg = (
32
- "TaskOutput image-list updates/removals has "
33
- "non-unique zarr_urls:"
32
+ "TaskOutput "
33
+ f"({len(self.image_list_updates)} image_list_updates and "
34
+ f"{len(self.image_list_removals)} image_list_removals) "
35
+ "has non-unique zarr_urls:"
34
36
  )
35
37
  for duplicate in duplicates:
36
38
  msg = f"{msg}\n{duplicate}"
@@ -1,4 +1 @@
1
- from .state import * # noqa: F401, F403
2
1
  from .user import * # noqa: F401, F403
3
- from .v1 import * # noqa: F401, F403
4
- from .v2 import * # noqa: F401, F403
@@ -1,12 +1,6 @@
1
1
  """
2
2
  Schemas for API request/response bodies
3
3
  """
4
- from ..state import _StateBase # noqa: F401
5
- from ..state import StateRead # noqa: F401
6
- from ..user import UserCreate # noqa: F401
7
- from ..user import UserRead # noqa: F401
8
- from ..user import UserUpdate # noqa: F401
9
- from ..user import UserUpdateStrict # noqa: F401
10
4
  from .applyworkflow import ApplyWorkflowCreateV1 # noqa: F401
11
5
  from .applyworkflow import ApplyWorkflowReadV1 # noqa: F401
12
6
  from .applyworkflow import ApplyWorkflowUpdateV1 # noqa: F401
@@ -1,4 +1,6 @@
1
1
  from .dataset import DatasetCreateV2 # noqa F401
2
+ from .dataset import DatasetExportV2 # noqa F401
3
+ from .dataset import DatasetImportV2 # noqa F401
2
4
  from .dataset import DatasetReadV2 # noqa F401
3
5
  from .dataset import DatasetUpdateV2 # noqa F401
4
6
  from .dumps import DatasetDumpV2 # noqa F401
@@ -103,6 +103,11 @@ class DatasetImportV2(BaseModel):
103
103
  images: list[SingleImage] = Field(default_factory=[])
104
104
  filters: Filters = Field(default_factory=Filters)
105
105
 
106
+ # Validators
107
+ @validator("zarr_dir")
108
+ def normalize_zarr_dir(cls, v: str) -> str:
109
+ return normalize_url(v)
110
+
106
111
 
107
112
  class DatasetExportV2(BaseModel):
108
113
  """
@@ -12,12 +12,14 @@ from .._validators import valdictkeys
12
12
  from .._validators import valint
13
13
  from ..v1.task import TaskExportV1
14
14
  from ..v1.task import TaskImportV1
15
- from ..v1.task import TaskReadV1
16
15
  from .task import TaskExportV2
17
16
  from .task import TaskImportV2
17
+ from .task import TaskLegacyReadV2
18
18
  from .task import TaskReadV2
19
19
  from fractal_server.images import Filters
20
20
 
21
+ RESERVED_ARGUMENTS = {"zarr_dir", "zarr_url", "zarr_urls", "init_args"}
22
+
21
23
 
22
24
  class WorkflowTaskStatusTypeV2(str, Enum):
23
25
  """
@@ -56,14 +58,36 @@ class WorkflowTaskCreateV2(BaseModel, extra=Extra.forbid):
56
58
  _meta_parallel = validator("meta_parallel", allow_reuse=True)(
57
59
  valdictkeys("meta_parallel")
58
60
  )
59
- _args_non_parallel = validator("args_non_parallel", allow_reuse=True)(
60
- valdictkeys("args_non_parallel")
61
- )
62
- _args_parallel = validator("args_parallel", allow_reuse=True)(
63
- valdictkeys("args_parallel")
64
- )
65
61
  _order = validator("order", allow_reuse=True)(valint("order", min_val=0))
66
62
 
63
+ @validator("args_non_parallel")
64
+ def validate_args_non_parallel(cls, value):
65
+ if value is None:
66
+ return
67
+ valdictkeys("args_non_parallel")(value)
68
+ args_keys = set(value.keys())
69
+ intersect_keys = RESERVED_ARGUMENTS.intersection(args_keys)
70
+ if intersect_keys:
71
+ raise ValueError(
72
+ "`args` contains the following forbidden keys: "
73
+ f"{intersect_keys}"
74
+ )
75
+ return value
76
+
77
+ @validator("args_parallel")
78
+ def validate_args_parallel(cls, value):
79
+ if value is None:
80
+ return
81
+ valdictkeys("args_parallel")(value)
82
+ args_keys = set(value.keys())
83
+ intersect_keys = RESERVED_ARGUMENTS.intersection(args_keys)
84
+ if intersect_keys:
85
+ raise ValueError(
86
+ "`args` contains the following forbidden keys: "
87
+ f"{intersect_keys}"
88
+ )
89
+ return value
90
+
67
91
  @root_validator
68
92
  def validate_legacy_task(cls, values):
69
93
  if values["is_legacy_task"] and (
@@ -96,7 +120,7 @@ class WorkflowTaskReadV2(BaseModel):
96
120
  task_id: Optional[int]
97
121
  task: Optional[TaskReadV2]
98
122
  task_legacy_id: Optional[int]
99
- task_legacy: Optional[TaskReadV1]
123
+ task_legacy: Optional[TaskLegacyReadV2]
100
124
 
101
125
 
102
126
  class WorkflowTaskUpdateV2(BaseModel):
@@ -114,12 +138,34 @@ class WorkflowTaskUpdateV2(BaseModel):
114
138
  _meta_parallel = validator("meta_parallel", allow_reuse=True)(
115
139
  valdictkeys("meta_parallel")
116
140
  )
117
- _args_non_parallel = validator("args_non_parallel", allow_reuse=True)(
118
- valdictkeys("args_non_parallel")
119
- )
120
- _args_parallel = validator("args_parallel", allow_reuse=True)(
121
- valdictkeys("args_parallel")
122
- )
141
+
142
+ @validator("args_non_parallel")
143
+ def validate_args_non_parallel(cls, value):
144
+ if value is None:
145
+ return
146
+ valdictkeys("args_non_parallel")(value)
147
+ args_keys = set(value.keys())
148
+ intersect_keys = RESERVED_ARGUMENTS.intersection(args_keys)
149
+ if intersect_keys:
150
+ raise ValueError(
151
+ "`args` contains the following forbidden keys: "
152
+ f"{intersect_keys}"
153
+ )
154
+ return value
155
+
156
+ @validator("args_parallel")
157
+ def validate_args_parallel(cls, value):
158
+ if value is None:
159
+ return
160
+ valdictkeys("args_parallel")(value)
161
+ args_keys = set(value.keys())
162
+ intersect_keys = RESERVED_ARGUMENTS.intersection(args_keys)
163
+ if intersect_keys:
164
+ raise ValueError(
165
+ "`args` contains the following forbidden keys: "
166
+ f"{intersect_keys}"
167
+ )
168
+ return value
123
169
 
124
170
 
125
171
  class WorkflowTaskImportV2(BaseModel):
@@ -33,7 +33,6 @@ def find_image_by_zarr_url(
33
33
  return dict(image=copy(images[ind]), index=ind)
34
34
 
35
35
 
36
- # FIXME: what is filters
37
36
  def match_filter(image: dict[str, Any], filters: Filters) -> bool:
38
37
  """
39
38
  Find whether an image matches a filter set.
@@ -15,8 +15,8 @@ from ..utils import slugify_task_name
15
15
  from ._TaskCollectPip import _TaskCollectPip
16
16
  from fractal_server.app.db import DBSyncSession
17
17
  from fractal_server.app.db import get_sync_db
18
- from fractal_server.app.models import State
19
- from fractal_server.app.models import Task
18
+ from fractal_server.app.models.v1 import State
19
+ from fractal_server.app.models.v1 import Task
20
20
  from fractal_server.app.schemas.v1 import TaskCollectStatusV1
21
21
  from fractal_server.app.schemas.v1 import TaskCreateV1
22
22
  from fractal_server.app.schemas.v1 import TaskReadV1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.0.0a11
3
+ Version: 2.0.0a12
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
@@ -1,16 +1,16 @@
1
- fractal_server/__init__.py,sha256=3s9YLGOYqSP6sC_lM9jIIhYieV3lgQqBsJSBVyRU0ZU,25
1
+ fractal_server/__init__.py,sha256=n09kNau5UUJa9hzn0KtsMdH-0FpJzIry0v93FUKYRLg,25
2
2
  fractal_server/__main__.py,sha256=CocbzZooX1UtGqPi55GcHGNxnrJXFg5tUU5b3wyFCyo,4958
3
3
  fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
4
4
  fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  fractal_server/app/db/__init__.py,sha256=WZEVfdJAX7ZyBM1ngfEGeqWWcjK_NygtCbawpmbwGpU,4042
6
- fractal_server/app/models/__init__.py,sha256=fwlIQsf-OLF2pPVQBxo43W0hD0gYIVmnCzPp9ekDo4g,144
6
+ fractal_server/app/models/__init__.py,sha256=FRRDJkx7LgPaUqSp8IV7qevu_VE2dt0-fQtQruHFaVo,267
7
7
  fractal_server/app/models/linkuserproject.py,sha256=eQaourbGRshvlMVlKzLYJKHEjfsW1CbWws9yW4eHXhA,567
8
8
  fractal_server/app/models/security.py,sha256=UG9wCVA5GRSyHrYEFhH8lIF1hXykxsr9LSi8_dFToMY,3378
9
9
  fractal_server/app/models/v1/__init__.py,sha256=qUlUGnWFaIm3aBXfUuLdhcW9f_s1VzAEuypr31zvHGo,458
10
10
  fractal_server/app/models/v1/dataset.py,sha256=99GDgt7njx8yYQApkImqp_7bHA5HH3ElvbR6Oyj9kVI,2017
11
11
  fractal_server/app/models/v1/job.py,sha256=QLGXcWdVRHaUHQNDapYYlLpEfw4K7QyD8TmcwhrWw2o,3304
12
12
  fractal_server/app/models/v1/project.py,sha256=sDmAFLOBK5o4dLrwsIN681JcT5J1rzoUNTV9QVqwnA8,859
13
- fractal_server/app/models/v1/state.py,sha256=W5XxCR9BlHXo5abCvzblkvufqpGZtM5-G11ixzMUOp4,1092
13
+ fractal_server/app/models/v1/state.py,sha256=ew7xw3iPzBwUnPlzmsOEMiPbPEMsJn_TyZ5cK93jBRQ,1095
14
14
  fractal_server/app/models/v1/task.py,sha256=3xZqNeFYUqslh8ddMSXF2nO4nIiOD8T5Ij37wY20kss,2782
15
15
  fractal_server/app/models/v1/workflow.py,sha256=dnY5eMaOe3oZv8arn00RNX9qVkBtTLG-vYdWXcQuyo4,3950
16
16
  fractal_server/app/models/v2/__init__.py,sha256=uLzdInqATSwi0bS_V4vKB-TqFrOFaXuxCAbU73c0f24,473
@@ -20,37 +20,37 @@ fractal_server/app/models/v2/job.py,sha256=ypJmN-qspkKBGhBG7Mt-HypSQqcQ2EmB4Bzzb
20
20
  fractal_server/app/models/v2/project.py,sha256=CqDEKzdVxmFDMee6DnVOyX7WGmdn-dQSLSekzw_OLUc,817
21
21
  fractal_server/app/models/v2/task.py,sha256=9ZPhug3VWyeqgT8wQ9_8ZXQ2crSiiicRipxrxTslOso,3257
22
22
  fractal_server/app/models/v2/workflow.py,sha256=YBgFGCziUgU0aJ5EM3Svu9W2c46AewZO9VBlFCHiSps,1069
23
- fractal_server/app/models/v2/workflowtask.py,sha256=kEm2k1LI0KK9vlTH7DL1NddaEUpIvMkFi42vahwDpd8,2695
23
+ fractal_server/app/models/v2/workflowtask.py,sha256=3jEkObsSnlI05Pur_dSsXYdJxRqPL60Z7tK5-EJLOks,1532
24
24
  fractal_server/app/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  fractal_server/app/routes/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- fractal_server/app/routes/admin/v1.py,sha256=uY6H1znlAlrM9e1MG2EThTqwciCl87Twew34JM5W6IU,13981
26
+ fractal_server/app/routes/admin/v1.py,sha256=uMupmRkicaoWazX8qSX5fgh00O3MbuSfim8QayP6NkE,13996
27
27
  fractal_server/app/routes/admin/v2.py,sha256=T8-bGAL25on-ntZx_Msz9j5jq6NGhkjVl1jp3eRJUbw,9830
28
28
  fractal_server/app/routes/api/__init__.py,sha256=EVyZrEq3I_1643QGTPCC5lgCp4xH_auYbrFfogTm4pc,315
29
29
  fractal_server/app/routes/api/v1/__init__.py,sha256=Y2HQdG197J0a7DyQEE2jn53IfxD0EHGhzK1I2JZuEck,958
30
- fractal_server/app/routes/api/v1/_aux_functions.py,sha256=eC5exnGj9jnJqx0ccecoNaipxDeK2ZsR1ev0syH5x-Y,11955
31
- fractal_server/app/routes/api/v1/dataset.py,sha256=7z57FGBTCyz_G6Ivr1PeGIXGyd15fs4iLD2aJUxnslA,16911
30
+ fractal_server/app/routes/api/v1/_aux_functions.py,sha256=KoSefKiBXximu0df4fJ3l9bKsGaLO8rb3z6xhD8PWj4,11973
31
+ fractal_server/app/routes/api/v1/dataset.py,sha256=HRE-8vPmVkeXf7WFYkI19mDtbY-iJZeJ7PmMiV0LMgY,16923
32
32
  fractal_server/app/routes/api/v1/job.py,sha256=NwXyhvvzdPDor0ts8Im__9-I0P1H943s4NXIRgaz7PM,5436
33
- fractal_server/app/routes/api/v1/project.py,sha256=keqA0gYM48lyFP8zJgZ6cv34V6Js8DD-gbzE316H46k,15765
34
- fractal_server/app/routes/api/v1/task.py,sha256=4zUXMtq5M95XjaZs1t9oibYHiDIwxpM-3sTAxN95aRk,6123
35
- fractal_server/app/routes/api/v1/task_collection.py,sha256=_cY3pPRGchdWPuJ1XudMZMVJ0IC0_XVH0XwLTiAbRGg,8873
36
- fractal_server/app/routes/api/v1/workflow.py,sha256=ZObifWTPi100oRQ1wEER8Sgsr3Neo8QVdCCFQnWMNZ0,10930
37
- fractal_server/app/routes/api/v1/workflowtask.py,sha256=ox-DIIqYV4K35hCu86eGa2SHnR5IQml-I00UHEwnmHQ,5579
33
+ fractal_server/app/routes/api/v1/project.py,sha256=4wlxcc-bR45bns6Yy0WyjA9Qv9eiMscATLZKjVhSd1k,15777
34
+ fractal_server/app/routes/api/v1/task.py,sha256=udbKnenzc-Q10elYCVB9JmOPWATraa9tZi0AaByvWo0,6129
35
+ fractal_server/app/routes/api/v1/task_collection.py,sha256=mFaYyCWtCPRqvs3j6zx_zaiDXn31Uzoa7UHZS-Lu_L0,8882
36
+ fractal_server/app/routes/api/v1/workflow.py,sha256=7r9IoIevg_rvYCrerMOsIsUabSOQatxdPCfLdkP0dRs,10942
37
+ fractal_server/app/routes/api/v1/workflowtask.py,sha256=qcHQlzlSFf_k8gtId-mA3tnyzgSR7i1m7pvR4R86blE,5582
38
38
  fractal_server/app/routes/api/v2/__init__.py,sha256=UNgODxoEXfQpQDjvsnMvHaUWbZOrcHhEXNisLcU-0tE,1487
39
39
  fractal_server/app/routes/api/v2/_aux_functions.py,sha256=IL1JKVqRcGfqiVbptDzpMKqi9QTYDYCCcsqIG0x0Nl8,14301
40
- fractal_server/app/routes/api/v2/dataset.py,sha256=0JGRnK1DRQKgVA3FDhK8VdoRglLYFxgkMQOaoWI-tiQ,7853
40
+ fractal_server/app/routes/api/v2/dataset.py,sha256=_HjKNP9XsMGoqyubGdF2ZyeW7vXC3VdK_0_TaUxgIF0,8248
41
41
  fractal_server/app/routes/api/v2/images.py,sha256=4r_HblPWyuKSZSJZfn8mbDaLv1ncwZU0gWdKneZcNG4,7894
42
- fractal_server/app/routes/api/v2/job.py,sha256=9mXaKCX_N3FXM0GIxdE49nWl_hJZ8CBLBIaMMhaCKOM,5334
43
- fractal_server/app/routes/api/v2/project.py,sha256=i9a19HAqE36N92G60ZYgObIP9nv-hR7Jt5nd9Dkhz1g,6024
42
+ fractal_server/app/routes/api/v2/job.py,sha256=BtaxErBDbLwjY2zgGD1I6eRpsffoMonifcS1CMEXmLU,5325
43
+ fractal_server/app/routes/api/v2/project.py,sha256=qyvizYZ4aUFgF3tGdfp4z8AwWgfo19N_KbFEljfUaC8,5594
44
44
  fractal_server/app/routes/api/v2/status.py,sha256=3bqQejJ3TnIMan5wK6jr9sv4ypsQr9WWU8xqlvTgDCE,5739
45
- fractal_server/app/routes/api/v2/submit.py,sha256=iszII5CvWDEjGPTphBgH9FVS1pNb5m11Xc8xozGgjgI,6901
45
+ fractal_server/app/routes/api/v2/submit.py,sha256=lbPTZKemoRjypcpExi-Yz9fIiPdv9OIjFeUu9yuILA4,6889
46
46
  fractal_server/app/routes/api/v2/task.py,sha256=gJ0LruSk-Q1iMw8ZOX8C0wrZ4S4DGlQTr_5SdJJud0Q,7130
47
- fractal_server/app/routes/api/v2/task_collection.py,sha256=O5eg40P-TwYy6azGh0DpyN6Rya9FfhRHQDf4qpYIGEE,8952
47
+ fractal_server/app/routes/api/v2/task_collection.py,sha256=wHkPkQKnvXEzidywuJqLe8QB_xjlHUqzPgsitXydNkU,8961
48
48
  fractal_server/app/routes/api/v2/task_legacy.py,sha256=P_VJv9v0yzFUBuS-DQHhMVSOe20ecGJJcFBqiiFciOM,1628
49
49
  fractal_server/app/routes/api/v2/workflow.py,sha256=sw-1phO_rrmDAcWX9Zqb9M8SfrWF78-02AuLB1-D1PU,11845
50
50
  fractal_server/app/routes/api/v2/workflowtask.py,sha256=l4eTD5IIun5cOdYzsxh3ajmnOISaSccYA_mVf15Cjtw,8802
51
51
  fractal_server/app/routes/auth.py,sha256=Xv80iqdyfY3lyicYs2Y8B6zEDEnyUu_H6_6psYtv3R4,4885
52
52
  fractal_server/app/routes/aux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- fractal_server/app/routes/aux/_job.py,sha256=5gKgvArAruSkMQuPN34Vvzi89WJbwWPsx0oDAa_iXu4,1248
53
+ fractal_server/app/routes/aux/_job.py,sha256=7OP3B_FqYJUQ8hVn-7G8oYEEaZrM4hxLF9FoSyjudTo,1251
54
54
  fractal_server/app/routes/aux/_runner.py,sha256=psW6fsoo_VrAHrD5UQPbqFYikCp0m16VRymC-U1yUTk,675
55
55
  fractal_server/app/runner/.gitignore,sha256=ytzN_oyHWXrGU7iFAtoHSTUbM6Rn6kG0Zkddg0xZk6s,16
56
56
  fractal_server/app/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -68,10 +68,10 @@ fractal_server/app/runner/executors/slurm/executor.py,sha256=O9h6ZPAKM95BUJrZkHC
68
68
  fractal_server/app/runner/executors/slurm/remote.py,sha256=wLziIsGdSMiO-jIXM8x77JRK82g_2hx0iBKTiMghuIo,5852
69
69
  fractal_server/app/runner/filenames.py,sha256=9lwu3yB4C67yiijYw8XIKaLFn3mJUt6_TCyVFM_aZUQ,206
70
70
  fractal_server/app/runner/set_start_and_last_task_index.py,sha256=-q4zVybAj8ek2XlbENKlfOAJ39hT_zoJoZkqzDqiAMY,1254
71
- fractal_server/app/runner/task_files.py,sha256=c5mggMy7BIK_yBUvbimFgvKFZPKKDu6RRfWepwinBVk,3219
72
- fractal_server/app/runner/v1/__init__.py,sha256=meqMG2UejFa_1hm5xlsmkDxsM7Y_hqftsexuteQXOrE,13608
73
- fractal_server/app/runner/v1/_common.py,sha256=fIt8BVW7u6ReZbHgQ5kV2kDtWoHMQNjPoeuWP5YfWQk,21240
74
- fractal_server/app/runner/v1/_local/__init__.py,sha256=8PjeyPLvj6KHdZ3HyzWZCdlrubgedA1hZLXGAsLNOKI,6926
71
+ fractal_server/app/runner/task_files.py,sha256=b5aRDi35QemBQnHT_AU6L_IPJJU_k_f5sJn-JXzkzy0,3180
72
+ fractal_server/app/runner/v1/__init__.py,sha256=Uqf9smd4G9JCunXOsJ0U_DJyhYvl8TwItY3TbDLBLMc,13620
73
+ fractal_server/app/runner/v1/_common.py,sha256=2-NScI-7qCIw14Od90so1onw-psIt8x1kx6EXq489Vk,21246
74
+ fractal_server/app/runner/v1/_local/__init__.py,sha256=ZcWftuGRLmN8-S6QZXm6FhNehsxwmwZRhuRv-a7zA6s,6839
75
75
  fractal_server/app/runner/v1/_local/_local_config.py,sha256=hM7SPxR07luXPcXdrWXRpEB2uOyjSSRUdqW3QBKJn9c,3147
76
76
  fractal_server/app/runner/v1/_local/_submit_setup.py,sha256=kvNPT7ey2mEamORzPMMVThbFHtzZcSr-0A9tYw9uVDA,1493
77
77
  fractal_server/app/runner/v1/_local/executor.py,sha256=QrJlD77G6q4WohoJQO7XXbvi2RlCUsNvMnPDEZIoAqA,3620
@@ -91,16 +91,16 @@ fractal_server/app/runner/v2/_slurm/get_slurm_config.py,sha256=I_lOS75iGYyJ74-gN
91
91
  fractal_server/app/runner/v2/deduplicate_list.py,sha256=-imwO7OB7ATADEnqVbTElUwoY0YIJCTf_SbWJNN9OZg,639
92
92
  fractal_server/app/runner/v2/handle_failed_job.py,sha256=M1r3dnrbUMo_AI2qjaVuGhieMAyLh5gcvB10YOBpjvI,5415
93
93
  fractal_server/app/runner/v2/merge_outputs.py,sha256=IHuHqbKmk97K35BFvTrKVBs60z3e_--OzXTnsvmA02c,1281
94
- fractal_server/app/runner/v2/runner.py,sha256=K6bmWbQRSZwbO6ZI2Bp7wNxYdkHcXxhWwBObMxJ0iSU,12599
95
- fractal_server/app/runner/v2/runner_functions.py,sha256=qVGG9KlH8ObX4Y0kr0q6qE8OpWFwf4RnOHhgPRRdj5M,10293
96
- fractal_server/app/runner/v2/runner_functions_low_level.py,sha256=djNKD1y_EE0Q9Jkzh1QdKpjM66JVsLQgX2_zJT0xQlA,3947
97
- fractal_server/app/runner/v2/task_interface.py,sha256=TZLVJs6CNFo2lFhr-lsDxe585cEhRv48eA490LS9aqc,1746
94
+ fractal_server/app/runner/v2/runner.py,sha256=pZDRYXibPs6lYz8hg-F7KbibR_cl0sjCa5s9yaFE_-s,14160
95
+ fractal_server/app/runner/v2/runner_functions.py,sha256=jwLfIL1gdmL-KqSKb-5CDG-g7rQYZdywn24bJ92R2qI,11067
96
+ fractal_server/app/runner/v2/runner_functions_low_level.py,sha256=nPxmoSTIAQRwEf8iwfCdwTq4Eo3eDAHcgGn3GejPgqk,3710
97
+ fractal_server/app/runner/v2/task_interface.py,sha256=myS-kT0DsJ8xIJZBVEzgD8g54VbiwL6i7Im3e1zcVHQ,1866
98
98
  fractal_server/app/runner/v2/v1_compat.py,sha256=t0ficzAHUFaaeI56nqTb4YEKxfARF7L9Y6ijtJCwjP8,912
99
- fractal_server/app/schemas/__init__.py,sha256=VL55f3CTFngXHYkOsFaLBEEkEEewEWI5ODlcGTI7cqA,157
99
+ fractal_server/app/schemas/__init__.py,sha256=jiIf54owztXupv3PO6Ilh0qcrkh2RUzKq4bcEFqEfc4,40
100
100
  fractal_server/app/schemas/_validators.py,sha256=1dTOYr1IZykrxuQSV2-zuEMZbKe_nGwrfS7iUrsh-sE,3461
101
101
  fractal_server/app/schemas/state.py,sha256=t4XM04aqxeluh8MfvD7LfEc-8-dOmUVluZHhLsfxxkc,692
102
102
  fractal_server/app/schemas/user.py,sha256=rE8WgBz-ceVUs0Sz2ZwcjUrSTZTnS0ys5SBtD2XD9r8,3113
103
- fractal_server/app/schemas/v1/__init__.py,sha256=gZLfkANl4YtZ7aV3PFoUj5w0m1-riQv9iRomJhZRLZo,2078
103
+ fractal_server/app/schemas/v1/__init__.py,sha256=terWiFLKJ0QU-y3PG3hXNYdpxGTffZTlwrwJ8LfQ3FM,1809
104
104
  fractal_server/app/schemas/v1/applyworkflow.py,sha256=uuIh7fHlHEL4yLqL-dePI6-nfCsqgBYATmht7w_KITw,4302
105
105
  fractal_server/app/schemas/v1/dataset.py,sha256=n71lNUO3JLy2K3IM9BZM2Fk1EnKQOTU7pm2s2rJ1FGY,3444
106
106
  fractal_server/app/schemas/v1/dumps.py,sha256=67VXnyLh_0Ufo7rPM2jZ9P9rk0CnYcVAkilx_cLX6sg,1274
@@ -109,8 +109,8 @@ fractal_server/app/schemas/v1/project.py,sha256=TO2TjI4m9FO-A9IB9lUCld7E4Ld0k4Ma
109
109
  fractal_server/app/schemas/v1/task.py,sha256=7BxOZ_qoRQ8n3YbQpDvB7VMcxB5fSYQmR5RLIWhuJ5U,3704
110
110
  fractal_server/app/schemas/v1/task_collection.py,sha256=uvq9bcMaGD_qHsh7YtcpoSAkVAbw12eY4DocIO3MKOg,3057
111
111
  fractal_server/app/schemas/v1/workflow.py,sha256=tuOs5E5Q_ozA8if7YPZ07cQjzqB_QMkBS4u92qo4Ro0,4618
112
- fractal_server/app/schemas/v2/__init__.py,sha256=zlCYrplCWwnCL9-BYsExRMfVzhBy21IMBfdHPMgJZYk,1752
113
- fractal_server/app/schemas/v2/dataset.py,sha256=MGv0bdzEIQFNy8ARqiDn_neC1mJJTMXFzbb9M5l4xxg,2474
112
+ fractal_server/app/schemas/v2/__init__.py,sha256=IssDWR6q_mgNkaAxfhSnEZZLZRZIqOsr9SM7RvN1IsY,1852
113
+ fractal_server/app/schemas/v2/dataset.py,sha256=dLT52tV4dSf2HrFNak4vdQEn8PT_04IUrGnd2z-AXIU,2599
114
114
  fractal_server/app/schemas/v2/dumps.py,sha256=IpIT_2KxJd7qTgW2NllDknGeP7vBAJDfyz1I5p3TytU,2023
115
115
  fractal_server/app/schemas/v2/job.py,sha256=zfF9K3v4jWUJ7M482ta2CkqUJ4tVT4XfVt60p9IRhP0,3250
116
116
  fractal_server/app/schemas/v2/manifest.py,sha256=N37IWohcfO3_y2l8rVM0h_1nZq7m4Izxk9iL1vtwBJw,6243
@@ -119,13 +119,13 @@ fractal_server/app/schemas/v2/status.py,sha256=SQaUpQkjFq5c5k5J4rOjNhuQaDOEg8lks
119
119
  fractal_server/app/schemas/v2/task.py,sha256=7IfxiZkaVqlARy7WYE_H8m7j_IEcuQaZORUrs6b5YuY,4672
120
120
  fractal_server/app/schemas/v2/task_collection.py,sha256=sY29NQfJrbjiidmVkVjSIH-20wIsmh7G1QOdr05KoDQ,3171
121
121
  fractal_server/app/schemas/v2/workflow.py,sha256=Zzx3e-qgkH8le0FUmAx9UrV5PWd7bj14PPXUh_zgZXM,1827
122
- fractal_server/app/schemas/v2/workflowtask.py,sha256=vRyPca8smu6fzwd9gO1eOd3qdPLJ-Zq2AAAbSLCou3I,5051
122
+ fractal_server/app/schemas/v2/workflowtask.py,sha256=atVuVN4aXsVEOmSd-vyg-8_8OnPmqx-gT75rXcn_AlQ,6552
123
123
  fractal_server/app/security/__init__.py,sha256=wxosoHc3mJYPCdPMyWnRD8w_2OgnKYp2aDkdmwrZh5k,11203
124
124
  fractal_server/config.py,sha256=CA8ASObADaME5chDiBXawAJZ3MvjTRpCKP0jvdYtSh8,15080
125
125
  fractal_server/data_migrations/README.md,sha256=_3AEFvDg9YkybDqCLlFPdDmGJvr6Tw7HRI14aZ3LOIw,398
126
126
  fractal_server/images/__init__.py,sha256=xO6jTLE4EZKO6cTDdJsBmK9cdeh9hFTaSbSuWgQg7y4,196
127
127
  fractal_server/images/models.py,sha256=9ipU5h4N6ogBChoB-2vHoqtL0TXOHCv6kRR-fER3mkM,4167
128
- fractal_server/images/tools.py,sha256=Q7jM60r_jq5bttrt1b4bU29n717RSUMMPbAbAkzWjgw,2234
128
+ fractal_server/images/tools.py,sha256=gxeniYy4Z-cp_ToK2LHPJUTVVUUrdpogYdcBUvBuLiY,2209
129
129
  fractal_server/logger.py,sha256=95duXY8eSxf1HWg0CVn8SUGNzgJw9ZR0FlapDDF6WAY,3924
130
130
  fractal_server/main.py,sha256=7CpwPfCsHxBAo5fWuXPCsYOFCpbBI0F7Z0jsgCQdou8,3001
131
131
  fractal_server/migrations/README,sha256=4rQvyDfqodGhpJw74VYijRmgFP49ji5chyEemWGHsuw,59
@@ -154,7 +154,7 @@ fractal_server/tasks/endpoint_operations.py,sha256=D1WSJd8dIfIumKezon1NYX5a0QNPq
154
154
  fractal_server/tasks/utils.py,sha256=R1_SKfXTwveT7CJJOrvkwi0vNpr9MBIiNh7qv8EK3Wc,3278
155
155
  fractal_server/tasks/v1/_TaskCollectPip.py,sha256=16Gn8lVYHBuwNLBHdcdx0X8s9QXXsbfPwSzcCcM6fRg,3775
156
156
  fractal_server/tasks/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
- fractal_server/tasks/v1/background_operations.py,sha256=T5L-ghgGEJIGcGoZB_r0cjH96UkEfAPkhr2ciTSaQlQ,11725
157
+ fractal_server/tasks/v1/background_operations.py,sha256=I-D4SaG56UMkoH7dNy5CzbEsAjySCPc7gQc-U14phKk,11731
158
158
  fractal_server/tasks/v1/get_collection_data.py,sha256=bi9tuApLgoKZNMIG1kR4GoKI9S6Y040gFfNQapw4ikM,502
159
159
  fractal_server/tasks/v2/_TaskCollectPip.py,sha256=QeCqXDgOnMjk3diVlC5bgGEywyQjYFm5637Rke49vJY,3775
160
160
  fractal_server/tasks/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -162,8 +162,8 @@ fractal_server/tasks/v2/background_operations.py,sha256=fUukEA-zFjUDhxgI3oO_Bvy7
162
162
  fractal_server/tasks/v2/get_collection_data.py,sha256=Qhf2T_aaqAfqu9_KpUSlXsS7EJoZQbEPEreHHa2jco8,502
163
163
  fractal_server/urls.py,sha256=5o_qq7PzKKbwq12NHSQZDmDitn5RAOeQ4xufu-2v9Zk,448
164
164
  fractal_server/utils.py,sha256=b7WwFdcFZ8unyT65mloFToYuEDXpQoHRcmRNqrhd_dQ,2115
165
- fractal_server-2.0.0a11.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
166
- fractal_server-2.0.0a11.dist-info/METADATA,sha256=vFS5XczcfUY_lJ0uxHE2LAm_9pwDShIGzl_jjC6Cwdo,4201
167
- fractal_server-2.0.0a11.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
168
- fractal_server-2.0.0a11.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
169
- fractal_server-2.0.0a11.dist-info/RECORD,,
165
+ fractal_server-2.0.0a12.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
166
+ fractal_server-2.0.0a12.dist-info/METADATA,sha256=vBB1xO15zGGC1KMJRwj7xmVLHS0pF0BLTjVUusilAns,4201
167
+ fractal_server-2.0.0a12.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
168
+ fractal_server-2.0.0a12.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
169
+ fractal_server-2.0.0a12.dist-info/RECORD,,