fractal-server 2.15.3__py3-none-any.whl → 2.15.5__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 (55) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/v2/project.py +0 -1
  3. fractal_server/app/models/v2/task_group.py +0 -1
  4. fractal_server/app/models/v2/workflow.py +0 -1
  5. fractal_server/app/routes/admin/v2/accounting.py +0 -1
  6. fractal_server/app/routes/admin/v2/job.py +27 -1
  7. fractal_server/app/routes/admin/v2/task.py +0 -4
  8. fractal_server/app/routes/admin/v2/task_group.py +0 -3
  9. fractal_server/app/routes/api/v2/_aux_functions.py +0 -1
  10. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -1
  11. fractal_server/app/routes/api/v2/history.py +0 -2
  12. fractal_server/app/routes/api/v2/images.py +0 -5
  13. fractal_server/app/routes/api/v2/pre_submission_checks.py +0 -1
  14. fractal_server/app/routes/api/v2/submit.py +0 -1
  15. fractal_server/app/routes/api/v2/task_collection_custom.py +0 -2
  16. fractal_server/app/routes/api/v2/task_collection_pixi.py +0 -1
  17. fractal_server/app/routes/api/v2/task_group.py +0 -2
  18. fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -2
  19. fractal_server/app/routes/api/v2/task_version_update.py +0 -2
  20. fractal_server/app/routes/api/v2/workflow.py +0 -2
  21. fractal_server/app/routes/auth/group.py +0 -5
  22. fractal_server/app/routes/pagination.py +0 -2
  23. fractal_server/app/runner/executors/local/runner.py +0 -3
  24. fractal_server/app/runner/v2/db_tools.py +0 -1
  25. fractal_server/app/runner/v2/merge_outputs.py +0 -2
  26. fractal_server/app/runner/v2/runner.py +0 -1
  27. fractal_server/app/runner/v2/task_interface.py +1 -4
  28. fractal_server/app/schemas/v2/accounting.py +0 -1
  29. fractal_server/app/schemas/v2/project.py +0 -3
  30. fractal_server/app/schemas/v2/workflow.py +0 -3
  31. fractal_server/config.py +2 -2
  32. fractal_server/data_migrations/2_14_10.py +0 -1
  33. fractal_server/main.py +0 -1
  34. fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -1
  35. fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -1
  36. fractal_server/tasks/v2/local/_utils.py +32 -0
  37. fractal_server/tasks/v2/local/collect.py +0 -1
  38. fractal_server/tasks/v2/local/collect_pixi.py +7 -6
  39. fractal_server/tasks/v2/local/deactivate.py +0 -2
  40. fractal_server/tasks/v2/local/deactivate_pixi.py +0 -1
  41. fractal_server/tasks/v2/local/reactivate_pixi.py +5 -0
  42. fractal_server/tasks/v2/ssh/_utils.py +38 -0
  43. fractal_server/tasks/v2/ssh/collect.py +0 -1
  44. fractal_server/tasks/v2/ssh/collect_pixi.py +11 -4
  45. fractal_server/tasks/v2/ssh/deactivate.py +0 -1
  46. fractal_server/tasks/v2/ssh/reactivate_pixi.py +10 -0
  47. fractal_server/tasks/v2/utils_pixi.py +67 -0
  48. fractal_server/tasks/v2/utils_python_interpreter.py +1 -1
  49. fractal_server/types/validators/_filter_validators.py +1 -1
  50. fractal_server/zip_tools.py +0 -1
  51. {fractal_server-2.15.3.dist-info → fractal_server-2.15.5.dist-info}/METADATA +2 -1
  52. {fractal_server-2.15.3.dist-info → fractal_server-2.15.5.dist-info}/RECORD +55 -55
  53. {fractal_server-2.15.3.dist-info → fractal_server-2.15.5.dist-info}/LICENSE +0 -0
  54. {fractal_server-2.15.3.dist-info → fractal_server-2.15.5.dist-info}/WHEEL +0 -0
  55. {fractal_server-2.15.3.dist-info → fractal_server-2.15.5.dist-info}/entry_points.txt +0 -0
@@ -1 +1 @@
1
- __VERSION__ = "2.15.3"
1
+ __VERSION__ = "2.15.5"
@@ -12,7 +12,6 @@ from fractal_server.utils import get_timestamp
12
12
 
13
13
 
14
14
  class ProjectV2(SQLModel, table=True):
15
-
16
15
  id: int | None = Field(default=None, primary_key=True)
17
16
  name: str
18
17
  timestamp_created: datetime = Field(
@@ -107,7 +107,6 @@ class TaskGroupV2(SQLModel, table=True):
107
107
 
108
108
 
109
109
  class TaskGroupActivityV2(SQLModel, table=True):
110
-
111
110
  id: int | None = Field(default=None, primary_key=True)
112
111
  user_id: int = Field(foreign_key="user_oauth.id")
113
112
  taskgroupv2_id: int | None = Field(
@@ -12,7 +12,6 @@ from .workflowtask import WorkflowTaskV2
12
12
 
13
13
 
14
14
  class WorkflowV2(SQLModel, table=True):
15
-
16
15
  id: int | None = Field(default=None, primary_key=True)
17
16
  name: str
18
17
  project_id: int = Field(foreign_key="projectv2.id", ondelete="CASCADE")
@@ -82,7 +82,6 @@ async def query_accounting_slurm(
82
82
  superuser: UserOAuth = Depends(current_active_superuser),
83
83
  db: AsyncSession = Depends(get_async_db),
84
84
  ) -> JSONResponse:
85
-
86
85
  stm = select(AccountingRecordSlurm.slurm_job_ids)
87
86
  if query.user_id is not None:
88
87
  stm = stm.where(AccountingRecordSlurm.user_id == query.user_id)
@@ -12,12 +12,15 @@ from sqlmodel import select
12
12
  from fractal_server.app.db import AsyncSession
13
13
  from fractal_server.app.db import get_async_db
14
14
  from fractal_server.app.models import UserOAuth
15
+ from fractal_server.app.models.v2 import HistoryRun
16
+ from fractal_server.app.models.v2 import HistoryUnit
15
17
  from fractal_server.app.models.v2 import JobV2
16
18
  from fractal_server.app.models.v2 import ProjectV2
17
19
  from fractal_server.app.routes.auth import current_active_superuser
18
20
  from fractal_server.app.routes.aux._job import _write_shutdown_file
19
21
  from fractal_server.app.routes.aux._runner import _check_shutdown_is_supported
20
22
  from fractal_server.app.runner.filenames import WORKFLOW_LOG_FILENAME
23
+ from fractal_server.app.schemas.v2 import HistoryUnitStatus
21
24
  from fractal_server.app.schemas.v2 import JobReadV2
22
25
  from fractal_server.app.schemas.v2 import JobStatusTypeV2
23
26
  from fractal_server.app.schemas.v2 import JobUpdateV2
@@ -111,7 +114,6 @@ async def view_single_job(
111
114
  user: UserOAuth = Depends(current_active_superuser),
112
115
  db: AsyncSession = Depends(get_async_db),
113
116
  ) -> JobReadV2:
114
-
115
117
  job = await db.get(JobV2, job_id)
116
118
  if not job:
117
119
  raise HTTPException(
@@ -149,6 +151,11 @@ async def update_job(
149
151
  status_code=status.HTTP_404_NOT_FOUND,
150
152
  detail=f"Job {job_id} not found",
151
153
  )
154
+ if job.status != JobStatusTypeV2.SUBMITTED:
155
+ raise HTTPException(
156
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
157
+ detail=f"Job {job_id} has status {job.status=} != 'submitted'.",
158
+ )
152
159
 
153
160
  if job_update.status != JobStatusTypeV2.FAILED:
154
161
  raise HTTPException(
@@ -165,6 +172,25 @@ async def update_job(
165
172
  f"{job.log or ''}\nThis job was manually marked as "
166
173
  f"'{JobStatusTypeV2.FAILED}' by an admin ({timestamp.isoformat()}).",
167
174
  )
175
+
176
+ res = await db.execute(
177
+ select(HistoryRun)
178
+ .where(HistoryRun.job_id == job_id)
179
+ .order_by(HistoryRun.timestamp_started.desc())
180
+ .limit(1)
181
+ )
182
+ latest_run = res.scalar_one_or_none()
183
+ if latest_run is not None:
184
+ setattr(latest_run, "status", HistoryUnitStatus.FAILED)
185
+ res = await db.execute(
186
+ select(HistoryUnit).where(
187
+ HistoryUnit.history_run_id == latest_run.id
188
+ )
189
+ )
190
+ history_units = res.scalars().all()
191
+ for history_unit in history_units:
192
+ setattr(history_unit, "status", HistoryUnitStatus.FAILED)
193
+
168
194
  await db.commit()
169
195
  await db.refresh(job)
170
196
  await db.close()
@@ -20,7 +20,6 @@ router = APIRouter()
20
20
 
21
21
 
22
22
  class TaskV2Minimal(BaseModel):
23
-
24
23
  id: int
25
24
  name: str
26
25
  type: str
@@ -32,13 +31,11 @@ class TaskV2Minimal(BaseModel):
32
31
 
33
32
 
34
33
  class ProjectUser(BaseModel):
35
-
36
34
  id: int
37
35
  email: EmailStr
38
36
 
39
37
 
40
38
  class TaskV2Relationship(BaseModel):
41
-
42
39
  workflow_id: int
43
40
  workflow_name: str
44
41
  project_id: int
@@ -47,7 +44,6 @@ class TaskV2Relationship(BaseModel):
47
44
 
48
45
 
49
46
  class TaskV2Info(BaseModel):
50
-
51
47
  task: TaskV2Minimal
52
48
  relationships: list[TaskV2Relationship]
53
49
 
@@ -43,7 +43,6 @@ async def get_task_group_activity_list(
43
43
  superuser: UserOAuth = Depends(current_active_superuser),
44
44
  db: AsyncSession = Depends(get_async_db),
45
45
  ) -> list[TaskGroupActivityV2Read]:
46
-
47
46
  stm = select(TaskGroupActivityV2)
48
47
  if task_group_activity_id is not None:
49
48
  stm = stm.where(TaskGroupActivityV2.id == task_group_activity_id)
@@ -73,7 +72,6 @@ async def query_task_group(
73
72
  user: UserOAuth = Depends(current_active_superuser),
74
73
  db: AsyncSession = Depends(get_async_db),
75
74
  ) -> TaskGroupReadV2:
76
-
77
75
  task_group = await db.get(TaskGroupV2, task_group_id)
78
76
  if task_group is None:
79
77
  raise HTTPException(
@@ -96,7 +94,6 @@ async def query_task_group_list(
96
94
  user: UserOAuth = Depends(current_active_superuser),
97
95
  db: AsyncSession = Depends(get_async_db),
98
96
  ) -> list[TaskGroupReadV2]:
99
-
100
97
  stm = select(TaskGroupV2)
101
98
 
102
99
  if user_group_id is not None and private is True:
@@ -333,7 +333,6 @@ async def _workflow_has_submitted_job(
333
333
  workflow_id: int,
334
334
  db: AsyncSession,
335
335
  ) -> bool:
336
-
337
336
  res = await db.execute(
338
337
  select(JobV2.id)
339
338
  .where(JobV2.status == JobStatusTypeV2.SUBMITTED)
@@ -224,7 +224,6 @@ async def _get_collection_task_group_activity_status_message(
224
224
  task_group_id: int,
225
225
  db: AsyncSession,
226
226
  ) -> str:
227
-
228
227
  res = await db.execute(
229
228
  select(TaskGroupActivityV2)
230
229
  .where(TaskGroupActivityV2.taskgroupv2_id == task_group_id)
@@ -71,7 +71,6 @@ async def get_workflow_tasks_statuses(
71
71
  user: UserOAuth = Depends(current_active_user),
72
72
  db: AsyncSession = Depends(get_async_db),
73
73
  ) -> JSONResponse:
74
-
75
74
  # Access control
76
75
  workflow = await _get_workflow_check_owner(
77
76
  project_id=project_id,
@@ -335,7 +334,6 @@ async def get_history_images(
335
334
  db: AsyncSession = Depends(get_async_db),
336
335
  pagination: PaginationRequest = Depends(get_pagination_params),
337
336
  ) -> ImagePage:
338
-
339
337
  # Access control and object retrieval
340
338
  wftask = await get_wftask_check_owner(
341
339
  project_id=project_id,
@@ -31,7 +31,6 @@ router = APIRouter()
31
31
 
32
32
 
33
33
  class ImagePage(PaginationResponse[SingleImage]):
34
-
35
34
  attributes: dict[str, list[ImageAttributeValue]]
36
35
  types: list[str]
37
36
 
@@ -64,7 +63,6 @@ async def post_new_image(
64
63
  user: UserOAuth = Depends(current_active_user),
65
64
  db: AsyncSession = Depends(get_async_db),
66
65
  ) -> Response:
67
-
68
66
  output = await _get_dataset_check_owner(
69
67
  project_id=project_id, dataset_id=dataset_id, user_id=user.id, db=db
70
68
  )
@@ -117,7 +115,6 @@ async def query_dataset_images(
117
115
  user: UserOAuth = Depends(current_active_user),
118
116
  db: AsyncSession = Depends(get_async_db),
119
117
  ) -> ImagePage:
120
-
121
118
  page = pagination.page
122
119
  page_size = pagination.page_size
123
120
 
@@ -131,7 +128,6 @@ async def query_dataset_images(
131
128
  types = aggregate_types(images)
132
129
 
133
130
  if query is not None:
134
-
135
131
  if query.zarr_url is not None:
136
132
  image = next(
137
133
  (
@@ -190,7 +186,6 @@ async def delete_dataset_images(
190
186
  user: UserOAuth = Depends(current_active_user),
191
187
  db: AsyncSession = Depends(get_async_db),
192
188
  ) -> Response:
193
-
194
189
  output = await _get_dataset_check_owner(
195
190
  project_id=project_id, dataset_id=dataset_id, user_id=user.id, db=db
196
191
  )
@@ -96,7 +96,6 @@ async def check_non_processed_images(
96
96
  user: UserOAuth = Depends(current_active_user),
97
97
  db: AsyncSession = Depends(get_async_db),
98
98
  ) -> JSONResponse:
99
-
100
99
  db_workflow_task, db_workflow = await _get_workflow_task_check_owner(
101
100
  project_id=project_id,
102
101
  workflow_task_id=workflowtask_id,
@@ -57,7 +57,6 @@ async def apply_workflow(
57
57
  user: UserOAuth = Depends(current_active_verified_user),
58
58
  db: AsyncSession = Depends(get_async_db),
59
59
  ) -> JobReadV2 | None:
60
-
61
60
  # Remove non-submitted V2 jobs from the app state when the list grows
62
61
  # beyond a threshold
63
62
  settings = Inject(get_settings)
@@ -47,7 +47,6 @@ async def collect_task_custom(
47
47
  user: UserOAuth = Depends(current_active_verified_user),
48
48
  db: AsyncSession = Depends(get_async_db),
49
49
  ) -> list[TaskReadV2]:
50
-
51
50
  settings = Inject(get_settings)
52
51
 
53
52
  # Validate query parameters related to user-group ownership
@@ -97,7 +96,6 @@ async def collect_task_custom(
97
96
  )
98
97
 
99
98
  if task_collect.package_root is None:
100
-
101
99
  package_name_underscore = task_collect.package_name.replace("-", "_")
102
100
  # Note that python_command is then used as part of a subprocess.run
103
101
  # statement: be careful with mixing `'` and `"`.
@@ -89,7 +89,6 @@ async def collect_task_pixi(
89
89
  user: UserOAuth = Depends(current_active_verified_user),
90
90
  db: AsyncSession = Depends(get_async_db),
91
91
  ) -> TaskGroupActivityV2Read:
92
-
93
92
  settings = Inject(get_settings)
94
93
  # Check if Pixi is available
95
94
  if settings.pixi is None:
@@ -72,7 +72,6 @@ async def get_task_group_activity_list(
72
72
  user: UserOAuth = Depends(current_active_user),
73
73
  db: AsyncSession = Depends(get_async_db),
74
74
  ) -> list[TaskGroupActivityV2Read]:
75
-
76
75
  stm = select(TaskGroupActivityV2).where(
77
76
  TaskGroupActivityV2.user_id == user.id
78
77
  )
@@ -105,7 +104,6 @@ async def get_task_group_activity(
105
104
  user: UserOAuth = Depends(current_active_user),
106
105
  db: AsyncSession = Depends(get_async_db),
107
106
  ) -> TaskGroupActivityV2Read:
108
-
109
107
  activity = await db.get(TaskGroupActivityV2, task_group_activity_id)
110
108
 
111
109
  if activity is None:
@@ -115,7 +115,6 @@ async def deactivate_task_group(
115
115
  # Submit background task
116
116
  settings = Inject(get_settings)
117
117
  if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
118
-
119
118
  # Validate user settings (backend-specific)
120
119
  user_settings = await validate_user_settings(
121
120
  user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
@@ -242,7 +241,6 @@ async def reactivate_task_group(
242
241
  # Submit background task
243
242
  settings = Inject(get_settings)
244
243
  if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
245
-
246
244
  # Validate user settings (backend-specific)
247
245
  user_settings = await validate_user_settings(
248
246
  user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
@@ -78,7 +78,6 @@ async def get_workflow_version_update_candidates(
78
78
  user: UserOAuth = Depends(current_active_user),
79
79
  db: AsyncSession = Depends(get_async_db),
80
80
  ) -> list[list[TaskVersionRead]]:
81
-
82
81
  workflow = await _get_workflow_check_owner(
83
82
  project_id=project_id,
84
83
  workflow_id=workflow_id,
@@ -181,7 +180,6 @@ async def replace_workflowtask(
181
180
  user: UserOAuth = Depends(current_active_user),
182
181
  db: AsyncSession = Depends(get_async_db),
183
182
  ) -> WorkflowTaskReadV2:
184
-
185
183
  # Get objects from database
186
184
  workflow_task, workflow = await _get_workflow_task_check_owner(
187
185
  project_id=project_id,
@@ -147,7 +147,6 @@ async def update_workflow(
147
147
 
148
148
  for key, value in patch.model_dump(exclude_unset=True).items():
149
149
  if key == "reordered_workflowtask_ids":
150
-
151
150
  if await _workflow_has_submitted_job(
152
151
  workflow_id=workflow_id, db=db
153
152
  ):
@@ -329,7 +328,6 @@ async def get_workflow_type_filters(
329
328
 
330
329
  response_items = []
331
330
  for wftask in workflow.task_list:
332
-
333
331
  # Compute input_type_filters, based on wftask and task manifest
334
332
  input_type_filters = merge_type_filters(
335
333
  wftask_type_filters=wftask.type_filters,
@@ -39,7 +39,6 @@ async def get_list_user_groups(
39
39
  user: UserOAuth = Depends(current_active_superuser),
40
40
  db: AsyncSession = Depends(get_async_db),
41
41
  ) -> list[UserGroupRead]:
42
-
43
42
  # Get all groups
44
43
  stm_all_groups = select(UserGroup)
45
44
  res = await db.execute(stm_all_groups)
@@ -88,7 +87,6 @@ async def create_single_group(
88
87
  user: UserOAuth = Depends(current_active_superuser),
89
88
  db: AsyncSession = Depends(get_async_db),
90
89
  ) -> UserGroupRead:
91
-
92
90
  # Check that name is not already in use
93
91
  existing_name_str = select(UserGroup).where(
94
92
  UserGroup.name == group_create.name
@@ -121,7 +119,6 @@ async def update_single_group(
121
119
  user: UserOAuth = Depends(current_active_superuser),
122
120
  db: AsyncSession = Depends(get_async_db),
123
121
  ) -> UserGroupRead:
124
-
125
122
  group = await _usergroup_or_404(group_id, db)
126
123
 
127
124
  # Patch `viewer_paths`
@@ -143,7 +140,6 @@ async def delete_single_group(
143
140
  user: UserOAuth = Depends(current_active_superuser),
144
141
  db: AsyncSession = Depends(get_async_db),
145
142
  ) -> Response:
146
-
147
143
  group = await _usergroup_or_404(group_id, db)
148
144
 
149
145
  if group.name == FRACTAL_DEFAULT_GROUP_NAME:
@@ -219,7 +215,6 @@ async def remove_user_from_group(
219
215
  superuser: UserOAuth = Depends(current_active_superuser),
220
216
  db: AsyncSession = Depends(get_async_db),
221
217
  ) -> UserGroupRead:
222
-
223
218
  # Check that user and group exist
224
219
  await _usergroup_or_404(group_id, db)
225
220
  user = await _user_or_404(user_id, db)
@@ -11,7 +11,6 @@ T = TypeVar("T")
11
11
 
12
12
 
13
13
  class PaginationRequest(BaseModel):
14
-
15
14
  page: int = Field(ge=1)
16
15
  page_size: int | None = Field(ge=1)
17
16
 
@@ -38,7 +37,6 @@ def get_pagination_params(
38
37
 
39
38
 
40
39
  class PaginationResponse(BaseModel, Generic[T]):
41
-
42
40
  current_page: int = Field(ge=1)
43
41
  page_size: int = Field(ge=0)
44
42
  total_count: int = Field(ge=0)
@@ -28,7 +28,6 @@ def run_single_task(
28
28
  parameters: dict[str, Any],
29
29
  task_files: TaskFiles,
30
30
  ):
31
-
32
31
  # Write args.json file
33
32
  with open(task_files.args_file_local, "w") as f:
34
33
  json.dump(parameters, f)
@@ -169,7 +168,6 @@ class LocalRunner(BaseRunner):
169
168
  exceptions: dict[int, BaseException] = {}
170
169
 
171
170
  try:
172
-
173
171
  self.validate_multisubmit_parameters(
174
172
  list_parameters=list_parameters,
175
173
  task_type=task_type,
@@ -208,7 +206,6 @@ class LocalRunner(BaseRunner):
208
206
 
209
207
  # Execute tasks, in chunks of size `parallel_tasks_per_job`
210
208
  for ind_chunk in range(0, n_elements, parallel_tasks_per_job):
211
-
212
209
  list_parameters_chunk = list_parameters[
213
210
  ind_chunk : ind_chunk + parallel_tasks_per_job
214
211
  ]
@@ -50,7 +50,6 @@ def bulk_update_status_of_history_unit(
50
50
  status: HistoryUnitStatus,
51
51
  db_sync: Session,
52
52
  ) -> None:
53
-
54
53
  len_history_unit_ids = len(history_unit_ids)
55
54
  logger.debug(
56
55
  f"[bulk_update_status_of_history_unit] {len_history_unit_ids=}."
@@ -3,7 +3,6 @@ from fractal_server.app.runner.v2.task_interface import TaskOutput
3
3
 
4
4
 
5
5
  def merge_outputs(task_outputs: list[TaskOutput]) -> TaskOutput:
6
-
7
6
  if len(task_outputs) == 0:
8
7
  return TaskOutput()
9
8
 
@@ -11,7 +10,6 @@ def merge_outputs(task_outputs: list[TaskOutput]) -> TaskOutput:
11
10
  final_image_list_removals = []
12
11
 
13
12
  for task_output in task_outputs:
14
-
15
13
  final_image_list_updates.extend(task_output.image_list_updates)
16
14
  final_image_list_removals.extend(task_output.image_list_removals)
17
15
 
@@ -415,7 +415,6 @@ def execute_tasks_v2(
415
415
  # Write current dataset images into the database.
416
416
  db_dataset = db.get(DatasetV2, dataset.id)
417
417
  if ENRICH_IMAGES_WITH_STATUS:
418
-
419
418
  db_dataset.images = _remove_status_from_attributes(tmp_images)
420
419
  else:
421
420
  db_dataset.images = tmp_images
@@ -11,7 +11,6 @@ from fractal_server.types import ZarrUrlStr
11
11
 
12
12
 
13
13
  class TaskOutput(BaseModel):
14
-
15
14
  model_config = ConfigDict(extra="forbid")
16
15
 
17
16
  image_list_updates: list[SingleImageTaskOutput] = Field(
@@ -40,7 +39,6 @@ class TaskOutput(BaseModel):
40
39
 
41
40
 
42
41
  class InitArgsModel(BaseModel):
43
-
44
42
  model_config = ConfigDict(extra="forbid")
45
43
 
46
44
  zarr_url: ZarrUrlStr
@@ -48,14 +46,13 @@ class InitArgsModel(BaseModel):
48
46
 
49
47
 
50
48
  class InitTaskOutput(BaseModel):
51
-
52
49
  model_config = ConfigDict(extra="forbid")
53
50
 
54
51
  parallelization_list: list[InitArgsModel] = Field(default_factory=list)
55
52
 
56
53
 
57
54
  def _cast_and_validate_TaskOutput(
58
- task_output: dict[str, Any]
55
+ task_output: dict[str, Any],
59
56
  ) -> TaskOutput | None:
60
57
  try:
61
58
  validated_task_output = TaskOutput(**task_output)
@@ -6,7 +6,6 @@ from pydantic.types import AwareDatetime
6
6
 
7
7
 
8
8
  class AccountingRecordRead(BaseModel):
9
-
10
9
  id: int
11
10
  user_id: int
12
11
  timestamp: AwareDatetime
@@ -9,14 +9,12 @@ from fractal_server.types import NonEmptyStr
9
9
 
10
10
 
11
11
  class ProjectCreateV2(BaseModel):
12
-
13
12
  model_config = ConfigDict(extra="forbid")
14
13
 
15
14
  name: NonEmptyStr
16
15
 
17
16
 
18
17
  class ProjectReadV2(BaseModel):
19
-
20
18
  id: int
21
19
  name: str
22
20
  timestamp_created: AwareDatetime
@@ -27,7 +25,6 @@ class ProjectReadV2(BaseModel):
27
25
 
28
26
 
29
27
  class ProjectUpdateV2(BaseModel):
30
-
31
28
  model_config = ConfigDict(extra="forbid")
32
29
 
33
30
  name: NonEmptyStr = None
@@ -17,14 +17,12 @@ from fractal_server.types import NonEmptyStr
17
17
 
18
18
 
19
19
  class WorkflowCreateV2(BaseModel):
20
-
21
20
  model_config = ConfigDict(extra="forbid")
22
21
 
23
22
  name: NonEmptyStr
24
23
 
25
24
 
26
25
  class WorkflowReadV2(BaseModel):
27
-
28
26
  id: int
29
27
  name: str
30
28
  project_id: int
@@ -42,7 +40,6 @@ class WorkflowReadV2WithWarnings(WorkflowReadV2):
42
40
 
43
41
 
44
42
  class WorkflowUpdateV2(BaseModel):
45
-
46
43
  model_config = ConfigDict(extra="forbid")
47
44
 
48
45
  name: NonEmptyStr = None
fractal_server/config.py CHANGED
@@ -95,10 +95,11 @@ class PixiSettings(BaseModel):
95
95
  PIXI_CONCURRENT_SOLVES: int = 4
96
96
  PIXI_CONCURRENT_DOWNLOADS: int = 4
97
97
  TOKIO_WORKER_THREADS: int = 2
98
+ DEFAULT_ENVIRONMENT: str = "default"
99
+ DEFAULT_PLATFORM: str = "linux-64"
98
100
 
99
101
  @model_validator(mode="after")
100
102
  def check_pixi_settings(self):
101
-
102
103
  if self.default_version not in self.versions:
103
104
  raise ValueError(
104
105
  f"Default version '{self.default_version}' not in "
@@ -108,7 +109,6 @@ class PixiSettings(BaseModel):
108
109
  pixi_base_dir = Path(self.versions[self.default_version]).parent
109
110
 
110
111
  for key, value in self.versions.items():
111
-
112
112
  pixi_path = Path(value)
113
113
 
114
114
  if pixi_path.parent != pixi_base_dir:
@@ -15,7 +15,6 @@ def fix_db():
15
15
  logger.info("START execution of fix_db function")
16
16
 
17
17
  with next(get_sync_db()) as db:
18
-
19
18
  stm = select(HistoryRun).order_by(HistoryRun.id)
20
19
  history_runs = db.execute(stm).scalars().all()
21
20
 
fractal_server/main.py CHANGED
@@ -83,7 +83,6 @@ async def lifespan(app: FastAPI):
83
83
  settings = Inject(get_settings)
84
84
 
85
85
  if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
86
-
87
86
  from fractal_server.ssh._fabric import FractalSSHList
88
87
 
89
88
  app.state.fractal_ssh_list = FractalSSHList()
@@ -33,7 +33,6 @@ TABLES_V1 = [
33
33
 
34
34
 
35
35
  def upgrade() -> None:
36
-
37
36
  logger = logging.getLogger("alembic.runtime.migration")
38
37
 
39
38
  target_metadata = SQLModel.metadata
@@ -18,7 +18,6 @@ depends_on = None
18
18
 
19
19
 
20
20
  def upgrade() -> None:
21
-
22
21
  with op.batch_alter_table("workflowtaskv2") as batch_op:
23
22
  batch_op.alter_column(
24
23
  "task_id", existing_type=sa.INTEGER(), nullable=False
@@ -1,11 +1,18 @@
1
1
  from pathlib import Path
2
2
 
3
+ from ..utils_pixi import simplify_pyproject_toml
3
4
  from fractal_server.app.schemas.v2 import TaskCreateV2
5
+ from fractal_server.config import get_settings
4
6
  from fractal_server.logger import get_logger
7
+ from fractal_server.logger import set_logger
8
+ from fractal_server.syringe import Inject
5
9
  from fractal_server.tasks.v2.utils_templates import customize_template
6
10
  from fractal_server.utils import execute_command_sync
7
11
 
8
12
 
13
+ logger = set_logger(__name__)
14
+
15
+
9
16
  def _customize_and_run_template(
10
17
  template_filename: str,
11
18
  replacements: list[tuple[str, str]],
@@ -69,3 +76,28 @@ def check_task_files_exist(task_list: list[TaskCreateV2]) -> None:
69
76
  f"Task `{_task.name}` has `command_parallel` "
70
77
  f"pointing to missing file `{_task_path}`."
71
78
  )
79
+
80
+
81
+ def edit_pyproject_toml_in_place_local(pyproject_toml_path: Path) -> None:
82
+ """
83
+ Wrapper of `simplify_pyproject_toml`, with I/O.
84
+ """
85
+
86
+ # Read `pyproject.toml`
87
+ with pyproject_toml_path.open() as f:
88
+ pyproject_contents = f.read()
89
+
90
+ # Simplify contents
91
+ settings = Inject(get_settings)
92
+ new_pyproject_contents = simplify_pyproject_toml(
93
+ original_toml_string=pyproject_contents,
94
+ pixi_environment=settings.pixi.DEFAULT_ENVIRONMENT,
95
+ pixi_platform=settings.pixi.DEFAULT_PLATFORM,
96
+ )
97
+ # Write new `pyproject.toml`
98
+ with pyproject_toml_path.open("w") as f:
99
+ f.write(new_pyproject_contents)
100
+ logger.debug(
101
+ f"Replaced local {pyproject_toml_path.as_posix()} "
102
+ "with simplified version."
103
+ )
@@ -99,7 +99,6 @@ def collect_local(
99
99
 
100
100
  # Write wheel file and set task_group.archive_path
101
101
  if wheel_file is not None:
102
-
103
102
  archive_path = (
104
103
  Path(task_group.path) / wheel_file.filename
105
104
  ).as_posix()