fractal-server 2.17.2__py3-none-any.whl → 2.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +2 -1
- fractal_server/app/models/linkuserproject.py +40 -0
- fractal_server/app/models/security.py +7 -5
- fractal_server/app/models/v2/job.py +13 -2
- fractal_server/app/models/v2/resource.py +13 -0
- fractal_server/app/routes/admin/v2/__init__.py +11 -11
- fractal_server/app/routes/admin/v2/accounting.py +2 -2
- fractal_server/app/routes/admin/v2/job.py +34 -23
- fractal_server/app/routes/admin/v2/sharing.py +103 -0
- fractal_server/app/routes/admin/v2/task.py +9 -8
- fractal_server/app/routes/admin/v2/task_group.py +94 -16
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +20 -20
- fractal_server/app/routes/api/__init__.py +0 -9
- fractal_server/app/routes/api/v2/__init__.py +47 -47
- fractal_server/app/routes/api/v2/_aux_functions.py +65 -64
- fractal_server/app/routes/api/v2/_aux_functions_history.py +8 -3
- fractal_server/app/routes/api/v2/_aux_functions_sharing.py +97 -0
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +4 -4
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +2 -2
- fractal_server/app/routes/api/v2/dataset.py +89 -77
- fractal_server/app/routes/api/v2/history.py +28 -16
- fractal_server/app/routes/api/v2/images.py +22 -8
- fractal_server/app/routes/api/v2/job.py +40 -24
- fractal_server/app/routes/api/v2/pre_submission_checks.py +13 -6
- fractal_server/app/routes/api/v2/project.py +48 -25
- fractal_server/app/routes/api/v2/sharing.py +311 -0
- fractal_server/app/routes/api/v2/status_legacy.py +22 -33
- fractal_server/app/routes/api/v2/submit.py +76 -71
- fractal_server/app/routes/api/v2/task.py +15 -17
- fractal_server/app/routes/api/v2/task_collection.py +18 -18
- fractal_server/app/routes/api/v2/task_collection_custom.py +11 -13
- fractal_server/app/routes/api/v2/task_collection_pixi.py +9 -9
- fractal_server/app/routes/api/v2/task_group.py +18 -18
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +26 -26
- fractal_server/app/routes/api/v2/task_version_update.py +12 -9
- fractal_server/app/routes/api/v2/workflow.py +41 -29
- fractal_server/app/routes/api/v2/workflow_import.py +25 -23
- fractal_server/app/routes/api/v2/workflowtask.py +25 -17
- fractal_server/app/routes/auth/_aux_auth.py +100 -0
- fractal_server/app/routes/auth/current_user.py +0 -63
- fractal_server/app/routes/auth/group.py +1 -30
- fractal_server/app/routes/auth/router.py +2 -0
- fractal_server/app/routes/auth/users.py +9 -0
- fractal_server/app/routes/auth/viewer_paths.py +43 -0
- fractal_server/app/schemas/user.py +29 -12
- fractal_server/app/schemas/user_group.py +0 -15
- fractal_server/app/schemas/v2/__init__.py +55 -48
- fractal_server/app/schemas/v2/dataset.py +35 -13
- fractal_server/app/schemas/v2/dumps.py +9 -9
- fractal_server/app/schemas/v2/job.py +11 -11
- fractal_server/app/schemas/v2/project.py +3 -3
- fractal_server/app/schemas/v2/resource.py +13 -4
- fractal_server/app/schemas/v2/sharing.py +99 -0
- fractal_server/app/schemas/v2/status_legacy.py +3 -3
- fractal_server/app/schemas/v2/task.py +6 -6
- fractal_server/app/schemas/v2/task_collection.py +4 -4
- fractal_server/app/schemas/v2/task_group.py +16 -16
- fractal_server/app/schemas/v2/workflow.py +16 -16
- fractal_server/app/schemas/v2/workflowtask.py +14 -14
- fractal_server/app/security/__init__.py +1 -1
- fractal_server/app/shutdown.py +6 -6
- fractal_server/config/__init__.py +0 -6
- fractal_server/config/_data.py +0 -79
- fractal_server/config/_main.py +6 -1
- fractal_server/data_migrations/2_18_0.py +30 -0
- fractal_server/images/models.py +1 -2
- fractal_server/main.py +72 -11
- fractal_server/migrations/versions/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +60 -0
- fractal_server/migrations/versions/88270f589c9b_add_prevent_new_submissions.py +39 -0
- fractal_server/migrations/versions/bc0e8b3327a7_project_sharing.py +72 -0
- fractal_server/migrations/versions/f0702066b007_one_submitted_job_per_dataset.py +40 -0
- fractal_server/runner/config/_slurm.py +2 -0
- fractal_server/runner/executors/slurm_common/_batching.py +4 -10
- fractal_server/runner/executors/slurm_common/slurm_config.py +1 -0
- fractal_server/runner/executors/slurm_ssh/runner.py +1 -1
- fractal_server/runner/executors/slurm_sudo/runner.py +1 -1
- fractal_server/runner/v2/_local.py +4 -3
- fractal_server/runner/v2/_slurm_ssh.py +4 -3
- fractal_server/runner/v2/_slurm_sudo.py +4 -3
- fractal_server/runner/v2/runner.py +36 -17
- fractal_server/runner/v2/runner_functions.py +11 -14
- fractal_server/runner/v2/submit_workflow.py +22 -9
- fractal_server/tasks/v2/local/_utils.py +2 -2
- fractal_server/tasks/v2/local/collect.py +5 -6
- fractal_server/tasks/v2/local/collect_pixi.py +5 -6
- fractal_server/tasks/v2/local/deactivate.py +7 -7
- fractal_server/tasks/v2/local/deactivate_pixi.py +3 -3
- fractal_server/tasks/v2/local/delete.py +5 -5
- fractal_server/tasks/v2/local/reactivate.py +5 -5
- fractal_server/tasks/v2/local/reactivate_pixi.py +5 -5
- fractal_server/tasks/v2/ssh/collect.py +5 -5
- fractal_server/tasks/v2/ssh/collect_pixi.py +5 -5
- fractal_server/tasks/v2/ssh/deactivate.py +7 -7
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +2 -2
- fractal_server/tasks/v2/ssh/delete.py +5 -5
- fractal_server/tasks/v2/ssh/reactivate.py +5 -5
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +5 -5
- fractal_server/tasks/v2/utils_background.py +7 -7
- fractal_server/tasks/v2/utils_database.py +5 -5
- fractal_server/types/__init__.py +22 -0
- fractal_server/types/validators/__init__.py +3 -0
- fractal_server/types/validators/_common_validators.py +32 -0
- {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/METADATA +3 -2
- {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/RECORD +108 -98
- {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,8 +5,8 @@ from tempfile import TemporaryDirectory
|
|
|
5
5
|
from fractal_server.app.db import get_sync_db
|
|
6
6
|
from fractal_server.app.models import Profile
|
|
7
7
|
from fractal_server.app.models import Resource
|
|
8
|
-
from fractal_server.app.schemas.v2 import
|
|
9
|
-
from fractal_server.app.schemas.v2 import
|
|
8
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityAction
|
|
9
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
|
|
10
10
|
from fractal_server.logger import reset_logger_handlers
|
|
11
11
|
from fractal_server.logger import set_logger
|
|
12
12
|
from fractal_server.ssh._fabric import SingleUseFractalSSH
|
|
@@ -150,7 +150,7 @@ def reactivate_ssh_pixi(
|
|
|
150
150
|
logger.info("installing - START")
|
|
151
151
|
|
|
152
152
|
# Set status to ONGOING and refresh logs
|
|
153
|
-
activity.status =
|
|
153
|
+
activity.status = TaskGroupActivityStatus.ONGOING
|
|
154
154
|
activity.log = get_current_log(log_file_path)
|
|
155
155
|
activity = add_commit_refresh(obj=activity, db=db)
|
|
156
156
|
|
|
@@ -164,7 +164,7 @@ def reactivate_ssh_pixi(
|
|
|
164
164
|
script_dir_remote=script_dir_remote,
|
|
165
165
|
prefix=(
|
|
166
166
|
f"{int(time.time())}_"
|
|
167
|
-
f"{
|
|
167
|
+
f"{TaskGroupActivityAction.REACTIVATE}"
|
|
168
168
|
),
|
|
169
169
|
logger_name=LOGGER_NAME,
|
|
170
170
|
fractal_ssh=fractal_ssh,
|
|
@@ -247,7 +247,7 @@ def reactivate_ssh_pixi(
|
|
|
247
247
|
activity = add_commit_refresh(obj=activity, db=db)
|
|
248
248
|
|
|
249
249
|
# Finalize (write metadata to DB)
|
|
250
|
-
activity.status =
|
|
250
|
+
activity.status = TaskGroupActivityStatus.OK
|
|
251
251
|
activity.timestamp_ended = get_timestamp()
|
|
252
252
|
activity = add_commit_refresh(obj=activity, db=db)
|
|
253
253
|
task_group.active = True
|
|
@@ -6,10 +6,10 @@ from sqlalchemy.orm import Session as DBSyncSession
|
|
|
6
6
|
|
|
7
7
|
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
8
8
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
9
|
-
from fractal_server.app.schemas.v2 import
|
|
10
|
-
from fractal_server.app.schemas.v2 import
|
|
9
|
+
from fractal_server.app.schemas.v2 import TaskCreate
|
|
10
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
|
|
11
11
|
from fractal_server.app.schemas.v2.manifest import ManifestV2
|
|
12
|
-
from fractal_server.app.schemas.v2.task_group import
|
|
12
|
+
from fractal_server.app.schemas.v2.task_group import TaskGroupActivityAction
|
|
13
13
|
from fractal_server.exceptions import UnreachableBranchError
|
|
14
14
|
from fractal_server.logger import get_logger
|
|
15
15
|
from fractal_server.logger import reset_logger_handlers
|
|
@@ -66,11 +66,11 @@ def fail_and_cleanup(
|
|
|
66
66
|
f"Original error: {str(exception)}"
|
|
67
67
|
)
|
|
68
68
|
|
|
69
|
-
task_group_activity.status =
|
|
69
|
+
task_group_activity.status = TaskGroupActivityStatus.FAILED
|
|
70
70
|
task_group_activity.timestamp_ended = get_timestamp()
|
|
71
71
|
task_group_activity.log = get_current_log(log_file_path)
|
|
72
72
|
task_group_activity = add_commit_refresh(obj=task_group_activity, db=db)
|
|
73
|
-
if task_group_activity.action ==
|
|
73
|
+
if task_group_activity.action == TaskGroupActivityAction.COLLECT:
|
|
74
74
|
db.delete(task_group)
|
|
75
75
|
db.commit()
|
|
76
76
|
reset_logger_handlers(logger)
|
|
@@ -83,7 +83,7 @@ def prepare_tasks_metadata(
|
|
|
83
83
|
python_bin: Path | None = None,
|
|
84
84
|
project_python_wrapper: Path | None = None,
|
|
85
85
|
package_version: str | None = None,
|
|
86
|
-
) -> list[
|
|
86
|
+
) -> list[TaskCreate]:
|
|
87
87
|
"""
|
|
88
88
|
Based on the package manifest and additional info, prepare the task list.
|
|
89
89
|
|
|
@@ -129,7 +129,7 @@ def prepare_tasks_metadata(
|
|
|
129
129
|
)
|
|
130
130
|
task_attributes["command_parallel"] = cmd_parallel
|
|
131
131
|
# Create object
|
|
132
|
-
task_obj =
|
|
132
|
+
task_obj = TaskCreate(
|
|
133
133
|
**_task.model_dump(
|
|
134
134
|
exclude={
|
|
135
135
|
"executable_non_parallel",
|
|
@@ -3,13 +3,13 @@ from sqlalchemy.orm import Session as DBSyncSession
|
|
|
3
3
|
|
|
4
4
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
5
5
|
from fractal_server.app.models.v2 import TaskV2
|
|
6
|
-
from fractal_server.app.schemas.v2 import
|
|
6
|
+
from fractal_server.app.schemas.v2 import TaskCreate
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def create_db_tasks_and_update_task_group_sync(
|
|
10
10
|
*,
|
|
11
11
|
task_group_id: int,
|
|
12
|
-
task_list: list[
|
|
12
|
+
task_list: list[TaskCreate],
|
|
13
13
|
db: DBSyncSession,
|
|
14
14
|
) -> TaskGroupV2:
|
|
15
15
|
"""
|
|
@@ -17,7 +17,7 @@ def create_db_tasks_and_update_task_group_sync(
|
|
|
17
17
|
|
|
18
18
|
Args:
|
|
19
19
|
task_group_id: ID of an existing `TaskGroupV2` object.
|
|
20
|
-
task_list: List of `
|
|
20
|
+
task_list: List of `TaskCreate` objects to be inserted into the db.
|
|
21
21
|
db: Synchronous database session
|
|
22
22
|
|
|
23
23
|
Returns:
|
|
@@ -36,7 +36,7 @@ def create_db_tasks_and_update_task_group_sync(
|
|
|
36
36
|
async def create_db_tasks_and_update_task_group_async(
|
|
37
37
|
*,
|
|
38
38
|
task_group_id: int,
|
|
39
|
-
task_list: list[
|
|
39
|
+
task_list: list[TaskCreate],
|
|
40
40
|
db: AsyncSession,
|
|
41
41
|
) -> TaskGroupV2:
|
|
42
42
|
"""
|
|
@@ -44,7 +44,7 @@ async def create_db_tasks_and_update_task_group_async(
|
|
|
44
44
|
|
|
45
45
|
Args:
|
|
46
46
|
task_group_id: ID of an existing `TaskGroupV2` object.
|
|
47
|
-
task_list: List of `
|
|
47
|
+
task_list: List of `TaskCreate` objects to be inserted into the db.
|
|
48
48
|
db: Synchronous database session
|
|
49
49
|
|
|
50
50
|
Returns:
|
fractal_server/types/__init__.py
CHANGED
|
@@ -10,6 +10,9 @@ from fractal_server.urls import normalize_url
|
|
|
10
10
|
|
|
11
11
|
from .validators import val_absolute_path
|
|
12
12
|
from .validators import val_http_url
|
|
13
|
+
from .validators import val_no_dotdot_in_path
|
|
14
|
+
from .validators import val_non_absolute_path
|
|
15
|
+
from .validators import val_os_path_normpath
|
|
13
16
|
from .validators import val_unique_list
|
|
14
17
|
from .validators import valdict_keys
|
|
15
18
|
from .validators import validate_attribute_filters
|
|
@@ -27,11 +30,22 @@ A non-empty string, with no leading/trailing whitespaces.
|
|
|
27
30
|
AbsolutePathStr = Annotated[
|
|
28
31
|
NonEmptyStr,
|
|
29
32
|
AfterValidator(val_absolute_path),
|
|
33
|
+
AfterValidator(val_no_dotdot_in_path),
|
|
34
|
+
AfterValidator(val_os_path_normpath),
|
|
30
35
|
]
|
|
31
36
|
"""
|
|
32
37
|
String representing an absolute path.
|
|
38
|
+
|
|
39
|
+
Validation fails if the path is not absolute or if it contains a
|
|
40
|
+
parent-directory reference "/../".
|
|
33
41
|
"""
|
|
34
42
|
|
|
43
|
+
RelativePathStr = Annotated[
|
|
44
|
+
NonEmptyStr,
|
|
45
|
+
AfterValidator(val_no_dotdot_in_path),
|
|
46
|
+
AfterValidator(val_os_path_normpath),
|
|
47
|
+
AfterValidator(val_non_absolute_path),
|
|
48
|
+
]
|
|
35
49
|
|
|
36
50
|
HttpUrlStr = Annotated[
|
|
37
51
|
NonEmptyStr,
|
|
@@ -44,19 +58,27 @@ String representing an URL.
|
|
|
44
58
|
|
|
45
59
|
ZarrUrlStr = Annotated[
|
|
46
60
|
NonEmptyStr,
|
|
61
|
+
AfterValidator(val_no_dotdot_in_path),
|
|
47
62
|
AfterValidator(normalize_url),
|
|
48
63
|
]
|
|
49
64
|
"""
|
|
50
65
|
String representing a zarr URL/path.
|
|
66
|
+
|
|
67
|
+
Validation fails if the path is not absolute or if it contains a
|
|
68
|
+
parent-directory reference "/../".
|
|
51
69
|
"""
|
|
52
70
|
|
|
53
71
|
|
|
54
72
|
ZarrDirStr = Annotated[
|
|
55
73
|
NonEmptyStr,
|
|
74
|
+
AfterValidator(val_no_dotdot_in_path),
|
|
56
75
|
AfterValidator(normalize_url),
|
|
57
76
|
]
|
|
58
77
|
"""
|
|
59
78
|
String representing a `zarr_dir` path.
|
|
79
|
+
|
|
80
|
+
Validation fails if the path is not absolute or if it contains a
|
|
81
|
+
parent-directory reference "/../".
|
|
60
82
|
"""
|
|
61
83
|
|
|
62
84
|
DictStrAny = Annotated[
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from ._common_validators import val_absolute_path # noqa F401
|
|
2
|
+
from ._common_validators import val_no_dotdot_in_path # noqa F401
|
|
3
|
+
from ._common_validators import val_os_path_normpath # noqa F401
|
|
2
4
|
from ._common_validators import val_http_url # noqa F401
|
|
3
5
|
from ._common_validators import val_unique_list # noqa F401
|
|
4
6
|
from ._common_validators import valdict_keys # noqa F401
|
|
7
|
+
from ._common_validators import val_non_absolute_path # noqa F401
|
|
5
8
|
from ._filter_validators import validate_attribute_filters # noqa F401
|
|
6
9
|
from ._workflow_task_arguments_validators import validate_wft_args # noqa F401
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
5
|
from pydantic import HttpUrl
|
|
@@ -29,6 +30,37 @@ def val_absolute_path(path: str) -> str:
|
|
|
29
30
|
return path
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def val_non_absolute_path(path: str) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Check that a string attribute is not an absolute path
|
|
36
|
+
"""
|
|
37
|
+
if os.path.isabs(path):
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"String must not be an absolute path (given '{path}')."
|
|
40
|
+
)
|
|
41
|
+
return path
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def val_no_dotdot_in_path(path: str) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Check that a string attribute has no '/../' in it
|
|
47
|
+
"""
|
|
48
|
+
if ".." in Path(path).parts:
|
|
49
|
+
raise ValueError("String must not contain '/../'.")
|
|
50
|
+
return path
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def val_os_path_normpath(path: str) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Apply `os.path.normpath` to `path`.
|
|
56
|
+
|
|
57
|
+
Note: we keep this separate from `fractal_server.urls.normalize_url`,
|
|
58
|
+
because this function only applies to on-disk paths, while `normalize_url`
|
|
59
|
+
may apply to s3 URLs as well.
|
|
60
|
+
"""
|
|
61
|
+
return os.path.normpath(path)
|
|
62
|
+
|
|
63
|
+
|
|
32
64
|
def val_unique_list(must_be_unique: list) -> list:
|
|
33
65
|
if len(set(must_be_unique)) != len(must_be_unique):
|
|
34
66
|
raise ValueError("List has repetitions")
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fractal-server
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.18.0
|
|
4
4
|
Summary: Backend component of the Fractal analytics platform
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Author: Tommaso Comparin
|
|
8
8
|
Author-email: tommaso.comparin@exact-lab.it
|
|
9
|
-
Requires-Python: >=3.11,<3.
|
|
9
|
+
Requires-Python: >=3.11,<3.15
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
15
|
Requires-Dist: alembic (>=1.13.1,<2.0.0)
|
|
15
16
|
Requires-Dist: fabric (>=3.2.2,<3.3.0)
|
|
16
17
|
Requires-Dist: fastapi (>=0.120.0,<0.121.0)
|