fractal-server 1.4.9__py3-none-any.whl → 2.0.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/app/models/__init__.py +4 -7
- fractal_server/app/models/linkuserproject.py +9 -0
- fractal_server/app/models/security.py +6 -0
- fractal_server/app/models/state.py +1 -1
- fractal_server/app/models/v1/__init__.py +10 -0
- fractal_server/app/models/{dataset.py → v1/dataset.py} +5 -5
- fractal_server/app/models/{job.py → v1/job.py} +5 -5
- fractal_server/app/models/{project.py → v1/project.py} +5 -5
- fractal_server/app/models/{task.py → v1/task.py} +7 -2
- fractal_server/app/models/{workflow.py → v1/workflow.py} +5 -5
- fractal_server/app/models/v2/__init__.py +20 -0
- fractal_server/app/models/v2/dataset.py +55 -0
- fractal_server/app/models/v2/job.py +51 -0
- fractal_server/app/models/v2/project.py +31 -0
- fractal_server/app/models/v2/task.py +93 -0
- fractal_server/app/models/v2/workflow.py +43 -0
- fractal_server/app/models/v2/workflowtask.py +90 -0
- fractal_server/app/routes/{admin.py → admin/v1.py} +42 -42
- fractal_server/app/routes/admin/v2.py +275 -0
- fractal_server/app/routes/api/v1/__init__.py +7 -7
- fractal_server/app/routes/api/v1/_aux_functions.py +2 -2
- fractal_server/app/routes/api/v1/dataset.py +44 -37
- fractal_server/app/routes/api/v1/job.py +12 -12
- fractal_server/app/routes/api/v1/project.py +23 -21
- fractal_server/app/routes/api/v1/task.py +24 -14
- fractal_server/app/routes/api/v1/task_collection.py +16 -14
- fractal_server/app/routes/api/v1/workflow.py +24 -24
- fractal_server/app/routes/api/v1/workflowtask.py +10 -10
- fractal_server/app/routes/api/v2/__init__.py +28 -0
- fractal_server/app/routes/api/v2/_aux_functions.py +497 -0
- fractal_server/app/routes/api/v2/apply.py +220 -0
- fractal_server/app/routes/api/v2/dataset.py +310 -0
- fractal_server/app/routes/api/v2/images.py +212 -0
- fractal_server/app/routes/api/v2/job.py +200 -0
- fractal_server/app/routes/api/v2/project.py +205 -0
- fractal_server/app/routes/api/v2/task.py +222 -0
- fractal_server/app/routes/api/v2/task_collection.py +229 -0
- fractal_server/app/routes/api/v2/workflow.py +398 -0
- fractal_server/app/routes/api/v2/workflowtask.py +269 -0
- fractal_server/app/routes/aux/_job.py +1 -1
- fractal_server/app/runner/async_wrap.py +27 -0
- fractal_server/app/runner/exceptions.py +129 -0
- fractal_server/app/runner/executors/local/__init__.py +3 -0
- fractal_server/app/runner/{_local → executors/local}/executor.py +2 -2
- fractal_server/app/runner/executors/slurm/__init__.py +3 -0
- fractal_server/app/runner/{_slurm → executors/slurm}/_batching.py +1 -1
- fractal_server/app/runner/executors/slurm/_check_jobs_status.py +72 -0
- fractal_server/app/runner/{_slurm → executors/slurm}/_executor_wait_thread.py +3 -4
- fractal_server/app/runner/{_slurm → executors/slurm}/_slurm_config.py +3 -152
- fractal_server/app/runner/{_slurm → executors/slurm}/_subprocess_run_as_user.py +1 -1
- fractal_server/app/runner/{_slurm → executors/slurm}/executor.py +9 -9
- fractal_server/app/runner/filenames.py +6 -0
- fractal_server/app/runner/set_start_and_last_task_index.py +39 -0
- fractal_server/app/runner/task_files.py +105 -0
- fractal_server/app/runner/{__init__.py → v1/__init__.py} +36 -49
- fractal_server/app/runner/{_common.py → v1/_common.py} +13 -120
- fractal_server/app/runner/{_local → v1/_local}/__init__.py +6 -6
- fractal_server/app/runner/{_local → v1/_local}/_local_config.py +6 -7
- fractal_server/app/runner/{_local → v1/_local}/_submit_setup.py +1 -5
- fractal_server/app/runner/v1/_slurm/__init__.py +310 -0
- fractal_server/app/runner/{_slurm → v1/_slurm}/_submit_setup.py +3 -9
- fractal_server/app/runner/v1/_slurm/get_slurm_config.py +163 -0
- fractal_server/app/runner/v1/common.py +117 -0
- fractal_server/app/runner/{handle_failed_job.py → v1/handle_failed_job.py} +8 -8
- fractal_server/app/runner/v2/__init__.py +337 -0
- fractal_server/app/runner/v2/_local/__init__.py +169 -0
- fractal_server/app/runner/v2/_local/_local_config.py +118 -0
- fractal_server/app/runner/v2/_local/_submit_setup.py +52 -0
- fractal_server/app/runner/v2/_slurm/__init__.py +157 -0
- fractal_server/app/runner/v2/_slurm/_submit_setup.py +83 -0
- fractal_server/app/runner/v2/_slurm/get_slurm_config.py +179 -0
- fractal_server/app/runner/v2/components.py +5 -0
- fractal_server/app/runner/v2/deduplicate_list.py +24 -0
- fractal_server/app/runner/v2/handle_failed_job.py +156 -0
- fractal_server/app/runner/v2/merge_outputs.py +41 -0
- fractal_server/app/runner/v2/runner.py +264 -0
- fractal_server/app/runner/v2/runner_functions.py +339 -0
- fractal_server/app/runner/v2/runner_functions_low_level.py +134 -0
- fractal_server/app/runner/v2/task_interface.py +43 -0
- fractal_server/app/runner/v2/v1_compat.py +21 -0
- fractal_server/app/schemas/__init__.py +4 -42
- fractal_server/app/schemas/v1/__init__.py +42 -0
- fractal_server/app/schemas/{applyworkflow.py → v1/applyworkflow.py} +18 -18
- fractal_server/app/schemas/{dataset.py → v1/dataset.py} +30 -30
- fractal_server/app/schemas/{dumps.py → v1/dumps.py} +8 -8
- fractal_server/app/schemas/{manifest.py → v1/manifest.py} +5 -5
- fractal_server/app/schemas/{project.py → v1/project.py} +9 -9
- fractal_server/app/schemas/{task.py → v1/task.py} +12 -12
- fractal_server/app/schemas/{task_collection.py → v1/task_collection.py} +7 -7
- fractal_server/app/schemas/{workflow.py → v1/workflow.py} +38 -38
- fractal_server/app/schemas/v2/__init__.py +34 -0
- fractal_server/app/schemas/v2/dataset.py +88 -0
- fractal_server/app/schemas/v2/dumps.py +87 -0
- fractal_server/app/schemas/v2/job.py +113 -0
- fractal_server/app/schemas/v2/manifest.py +109 -0
- fractal_server/app/schemas/v2/project.py +36 -0
- fractal_server/app/schemas/v2/task.py +121 -0
- fractal_server/app/schemas/v2/task_collection.py +105 -0
- fractal_server/app/schemas/v2/workflow.py +78 -0
- fractal_server/app/schemas/v2/workflowtask.py +118 -0
- fractal_server/config.py +5 -10
- fractal_server/images/__init__.py +50 -0
- fractal_server/images/tools.py +86 -0
- fractal_server/main.py +11 -3
- fractal_server/migrations/versions/4b35c5cefbe3_tmp_is_v2_compatible.py +39 -0
- fractal_server/migrations/versions/56af171b0159_v2.py +217 -0
- fractal_server/migrations/versions/876f28db9d4e_tmp_split_task_and_wftask_meta.py +68 -0
- fractal_server/migrations/versions/974c802f0dd0_tmp_workflowtaskv2_type_in_db.py +37 -0
- fractal_server/migrations/versions/9cd305cd6023_tmp_workflowtaskv2.py +40 -0
- fractal_server/migrations/versions/a6231ed6273c_tmp_args_schemas_in_taskv2.py +42 -0
- fractal_server/migrations/versions/b9e9eed9d442_tmp_taskv2_type.py +37 -0
- fractal_server/migrations/versions/e3e639454d4b_tmp_make_task_meta_non_optional.py +50 -0
- fractal_server/tasks/__init__.py +0 -5
- fractal_server/tasks/endpoint_operations.py +13 -19
- fractal_server/tasks/utils.py +35 -0
- fractal_server/tasks/{_TaskCollectPip.py → v1/_TaskCollectPip.py} +3 -3
- fractal_server/tasks/{background_operations.py → v1/background_operations.py} +18 -50
- fractal_server/tasks/v1/get_collection_data.py +14 -0
- fractal_server/tasks/v2/_TaskCollectPip.py +103 -0
- fractal_server/tasks/v2/background_operations.py +382 -0
- fractal_server/tasks/v2/get_collection_data.py +14 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/METADATA +3 -4
- fractal_server-2.0.0a0.dist-info/RECORD +166 -0
- fractal_server/app/runner/_slurm/.gitignore +0 -2
- fractal_server/app/runner/_slurm/__init__.py +0 -150
- fractal_server/app/runner/common.py +0 -311
- fractal_server-1.4.9.dist-info/RECORD +0 -97
- /fractal_server/app/runner/{_slurm → executors/slurm}/remote.py +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/LICENSE +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/WHEEL +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
"""TMP - split Task and WFTask meta
|
2
|
+
|
3
|
+
Revision ID: 876f28db9d4e
|
4
|
+
Revises: b9e9eed9d442
|
5
|
+
Create Date: 2024-03-27 14:35:50.076725
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
from alembic import op
|
10
|
+
from sqlalchemy.dialects import postgresql
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "876f28db9d4e"
|
14
|
+
down_revision = "b9e9eed9d442"
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade() -> None:
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
22
|
+
batch_op.add_column(
|
23
|
+
sa.Column("meta_non_parallel", sa.JSON(), nullable=True)
|
24
|
+
)
|
25
|
+
batch_op.add_column(
|
26
|
+
sa.Column("meta_parallel", sa.JSON(), nullable=True)
|
27
|
+
)
|
28
|
+
batch_op.drop_column("meta")
|
29
|
+
|
30
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
31
|
+
batch_op.add_column(
|
32
|
+
sa.Column("meta_parallel", sa.JSON(), nullable=True)
|
33
|
+
)
|
34
|
+
batch_op.add_column(
|
35
|
+
sa.Column("meta_non_parallel", sa.JSON(), nullable=True)
|
36
|
+
)
|
37
|
+
batch_op.drop_column("meta")
|
38
|
+
|
39
|
+
# ### end Alembic commands ###
|
40
|
+
|
41
|
+
|
42
|
+
def downgrade() -> None:
|
43
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
44
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
45
|
+
batch_op.add_column(
|
46
|
+
sa.Column(
|
47
|
+
"meta",
|
48
|
+
postgresql.JSON(astext_type=sa.Text()),
|
49
|
+
autoincrement=False,
|
50
|
+
nullable=True,
|
51
|
+
)
|
52
|
+
)
|
53
|
+
batch_op.drop_column("meta_non_parallel")
|
54
|
+
batch_op.drop_column("meta_parallel")
|
55
|
+
|
56
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
57
|
+
batch_op.add_column(
|
58
|
+
sa.Column(
|
59
|
+
"meta",
|
60
|
+
postgresql.JSON(astext_type=sa.Text()),
|
61
|
+
autoincrement=False,
|
62
|
+
nullable=True,
|
63
|
+
)
|
64
|
+
)
|
65
|
+
batch_op.drop_column("meta_parallel")
|
66
|
+
batch_op.drop_column("meta_non_parallel")
|
67
|
+
|
68
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"""TMP - WorkflowTaskV2.type in DB
|
2
|
+
|
3
|
+
Revision ID: 974c802f0dd0
|
4
|
+
Revises: e3e639454d4b
|
5
|
+
Create Date: 2024-04-02 11:21:20.212530
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
import sqlmodel
|
10
|
+
from alembic import op
|
11
|
+
|
12
|
+
|
13
|
+
# revision identifiers, used by Alembic.
|
14
|
+
revision = "974c802f0dd0"
|
15
|
+
down_revision = "e3e639454d4b"
|
16
|
+
branch_labels = None
|
17
|
+
depends_on = None
|
18
|
+
|
19
|
+
|
20
|
+
def upgrade() -> None:
|
21
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
22
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
23
|
+
batch_op.add_column(
|
24
|
+
sa.Column(
|
25
|
+
"task_type", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
# ### end Alembic commands ###
|
30
|
+
|
31
|
+
|
32
|
+
def downgrade() -> None:
|
33
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
34
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
35
|
+
batch_op.drop_column("task_type")
|
36
|
+
|
37
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""TMP - WorkflowTaskV2
|
2
|
+
|
3
|
+
Revision ID: 9cd305cd6023
|
4
|
+
Revises: a6231ed6273c
|
5
|
+
Create Date: 2024-03-26 09:15:00.188036
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
from alembic import op
|
10
|
+
from sqlalchemy.dialects import sqlite
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "9cd305cd6023"
|
14
|
+
down_revision = "a6231ed6273c"
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade() -> None:
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
22
|
+
batch_op.add_column(
|
23
|
+
sa.Column("args_parallel", sa.JSON(), nullable=True)
|
24
|
+
)
|
25
|
+
batch_op.add_column(
|
26
|
+
sa.Column("args_non_parallel", sa.JSON(), nullable=True)
|
27
|
+
)
|
28
|
+
batch_op.drop_column("args")
|
29
|
+
|
30
|
+
# ### end Alembic commands ###
|
31
|
+
|
32
|
+
|
33
|
+
def downgrade() -> None:
|
34
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
35
|
+
with op.batch_alter_table("workflowtaskv2", schema=None) as batch_op:
|
36
|
+
batch_op.add_column(sa.Column("args", sqlite.JSON(), nullable=True))
|
37
|
+
batch_op.drop_column("args_non_parallel")
|
38
|
+
batch_op.drop_column("args_parallel")
|
39
|
+
|
40
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,42 @@
|
|
1
|
+
"""TMP - Args Schemas in TaskV2
|
2
|
+
|
3
|
+
Revision ID: a6231ed6273c
|
4
|
+
Revises: 56af171b0159
|
5
|
+
Create Date: 2024-03-26 08:49:51.870087
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
from alembic import op
|
10
|
+
from sqlalchemy.dialects import sqlite
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "a6231ed6273c"
|
14
|
+
down_revision = "56af171b0159"
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade() -> None:
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
22
|
+
batch_op.add_column(
|
23
|
+
sa.Column("args_schema_non_parallel", sa.JSON(), nullable=True)
|
24
|
+
)
|
25
|
+
batch_op.add_column(
|
26
|
+
sa.Column("args_schema_parallel", sa.JSON(), nullable=True)
|
27
|
+
)
|
28
|
+
batch_op.drop_column("args_schema")
|
29
|
+
|
30
|
+
# ### end Alembic commands ###
|
31
|
+
|
32
|
+
|
33
|
+
def downgrade() -> None:
|
34
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
35
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
36
|
+
batch_op.add_column(
|
37
|
+
sa.Column("args_schema", sqlite.JSON(), nullable=True)
|
38
|
+
)
|
39
|
+
batch_op.drop_column("args_schema_parallel")
|
40
|
+
batch_op.drop_column("args_schema_non_parallel")
|
41
|
+
|
42
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"""TMP - TaskV2.type
|
2
|
+
|
3
|
+
Revision ID: b9e9eed9d442
|
4
|
+
Revises: 9cd305cd6023
|
5
|
+
Create Date: 2024-03-27 13:10:34.125503
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
import sqlmodel
|
10
|
+
from alembic import op
|
11
|
+
|
12
|
+
|
13
|
+
# revision identifiers, used by Alembic.
|
14
|
+
revision = "b9e9eed9d442"
|
15
|
+
down_revision = "9cd305cd6023"
|
16
|
+
branch_labels = None
|
17
|
+
depends_on = None
|
18
|
+
|
19
|
+
|
20
|
+
def upgrade() -> None:
|
21
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
22
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
23
|
+
batch_op.add_column(
|
24
|
+
sa.Column(
|
25
|
+
"type", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
# ### end Alembic commands ###
|
30
|
+
|
31
|
+
|
32
|
+
def downgrade() -> None:
|
33
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
34
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
35
|
+
batch_op.drop_column("type")
|
36
|
+
|
37
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""TMP - make task.meta non optional
|
2
|
+
|
3
|
+
Revision ID: e3e639454d4b
|
4
|
+
Revises: 4b35c5cefbe3
|
5
|
+
Create Date: 2024-03-29 17:10:57.643561
|
6
|
+
|
7
|
+
"""
|
8
|
+
import sqlalchemy as sa
|
9
|
+
from alembic import op
|
10
|
+
from sqlalchemy.dialects import postgresql
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "e3e639454d4b"
|
14
|
+
down_revision = "4b35c5cefbe3"
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade() -> None:
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
22
|
+
batch_op.alter_column(
|
23
|
+
"meta_non_parallel",
|
24
|
+
existing_type=postgresql.JSON(astext_type=sa.Text()),
|
25
|
+
nullable=False,
|
26
|
+
)
|
27
|
+
batch_op.alter_column(
|
28
|
+
"meta_parallel",
|
29
|
+
existing_type=postgresql.JSON(astext_type=sa.Text()),
|
30
|
+
nullable=False,
|
31
|
+
)
|
32
|
+
|
33
|
+
# ### end Alembic commands ###
|
34
|
+
|
35
|
+
|
36
|
+
def downgrade() -> None:
|
37
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
38
|
+
with op.batch_alter_table("taskv2", schema=None) as batch_op:
|
39
|
+
batch_op.alter_column(
|
40
|
+
"meta_parallel",
|
41
|
+
existing_type=postgresql.JSON(astext_type=sa.Text()),
|
42
|
+
nullable=True,
|
43
|
+
)
|
44
|
+
batch_op.alter_column(
|
45
|
+
"meta_non_parallel",
|
46
|
+
existing_type=postgresql.JSON(astext_type=sa.Text()),
|
47
|
+
nullable=True,
|
48
|
+
)
|
49
|
+
|
50
|
+
# ### end Alembic commands ###
|
fractal_server/tasks/__init__.py
CHANGED
@@ -4,35 +4,26 @@ from typing import Optional
|
|
4
4
|
from typing import Union
|
5
5
|
from zipfile import ZipFile
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from .utils import _normalize_package_name
|
8
|
+
from .utils import get_python_interpreter
|
9
|
+
from .v1._TaskCollectPip import _TaskCollectPip as _TaskCollectPipV1
|
10
|
+
from .v2._TaskCollectPip import _TaskCollectPip as _TaskCollectPipV2
|
11
|
+
from fractal_server.app.schemas.v1 import ManifestV1
|
12
|
+
from fractal_server.app.schemas.v2 import ManifestV2
|
9
13
|
from fractal_server.config import get_settings
|
10
14
|
from fractal_server.logger import get_logger
|
11
15
|
from fractal_server.syringe import Inject
|
12
|
-
from fractal_server.tasks._TaskCollectPip import _TaskCollectPip
|
13
|
-
from fractal_server.tasks.utils import _normalize_package_name
|
14
|
-
from fractal_server.tasks.utils import get_absolute_venv_path
|
15
|
-
from fractal_server.tasks.utils import get_collection_path
|
16
|
-
from fractal_server.tasks.utils import get_python_interpreter
|
17
16
|
from fractal_server.utils import execute_command
|
18
17
|
|
19
18
|
|
20
19
|
FRACTAL_PUBLIC_TASK_SUBDIR = ".fractal"
|
21
20
|
|
22
21
|
|
23
|
-
def get_collection_data(venv_path: Path) -> TaskCollectStatus:
|
24
|
-
package_path = get_absolute_venv_path(venv_path)
|
25
|
-
collection_path = get_collection_path(package_path)
|
26
|
-
with collection_path.open() as f:
|
27
|
-
data = json.load(f)
|
28
|
-
return TaskCollectStatus(**data)
|
29
|
-
|
30
|
-
|
31
22
|
async def download_package(
|
32
23
|
*,
|
33
|
-
task_pkg:
|
24
|
+
task_pkg: Union[_TaskCollectPipV1, _TaskCollectPipV2],
|
34
25
|
dest: Union[str, Path],
|
35
|
-
):
|
26
|
+
) -> Path:
|
36
27
|
"""
|
37
28
|
Download package to destination
|
38
29
|
"""
|
@@ -52,7 +43,7 @@ async def download_package(
|
|
52
43
|
|
53
44
|
def _load_manifest_from_wheel(
|
54
45
|
path: Path, wheel: ZipFile, logger_name: Optional[str] = None
|
55
|
-
) -> ManifestV1:
|
46
|
+
) -> Union[ManifestV1, ManifestV2]:
|
56
47
|
logger = get_logger(logger_name)
|
57
48
|
namelist = wheel.namelist()
|
58
49
|
try:
|
@@ -69,6 +60,9 @@ def _load_manifest_from_wheel(
|
|
69
60
|
if manifest_version == "1":
|
70
61
|
pkg_manifest = ManifestV1(**manifest_dict)
|
71
62
|
return pkg_manifest
|
63
|
+
elif manifest_version == "2":
|
64
|
+
pkg_manifest = ManifestV2(**manifest_dict)
|
65
|
+
return pkg_manifest
|
72
66
|
else:
|
73
67
|
msg = f"Manifest version {manifest_version=} not supported"
|
74
68
|
logger.error(msg)
|
@@ -146,7 +140,7 @@ def inspect_package(path: Path, logger_name: Optional[str] = None) -> dict:
|
|
146
140
|
|
147
141
|
def create_package_dir_pip(
|
148
142
|
*,
|
149
|
-
task_pkg:
|
143
|
+
task_pkg: Union[_TaskCollectPipV1, _TaskCollectPipV2],
|
150
144
|
create: bool = True,
|
151
145
|
) -> Path:
|
152
146
|
"""
|
fractal_server/tasks/utils.py
CHANGED
@@ -5,7 +5,9 @@ from pathlib import Path
|
|
5
5
|
from typing import Optional
|
6
6
|
|
7
7
|
from fractal_server.config import get_settings
|
8
|
+
from fractal_server.logger import get_logger
|
8
9
|
from fractal_server.syringe import Inject
|
10
|
+
from fractal_server.utils import execute_command
|
9
11
|
|
10
12
|
COLLECTION_FILENAME = "collection.json"
|
11
13
|
COLLECTION_LOG_FILENAME = "collection.log"
|
@@ -84,3 +86,36 @@ def _normalize_package_name(name: str) -> str:
|
|
84
86
|
The normalized package name.
|
85
87
|
"""
|
86
88
|
return re.sub(r"[-_.]+", "-", name).lower()
|
89
|
+
|
90
|
+
|
91
|
+
async def _init_venv(
|
92
|
+
*,
|
93
|
+
path: Path,
|
94
|
+
python_version: Optional[str] = None,
|
95
|
+
logger_name: str,
|
96
|
+
) -> Path:
|
97
|
+
"""
|
98
|
+
Set a virtual environment at `path/venv`
|
99
|
+
|
100
|
+
Args:
|
101
|
+
path : Path
|
102
|
+
path to directory in which to set up the virtual environment
|
103
|
+
python_version : default=None
|
104
|
+
Python version the virtual environment will be based upon
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
python_bin : Path
|
108
|
+
path to python interpreter
|
109
|
+
"""
|
110
|
+
logger = get_logger(logger_name)
|
111
|
+
logger.debug(f"[_init_venv] {path=}")
|
112
|
+
interpreter = get_python_interpreter(version=python_version)
|
113
|
+
logger.debug(f"[_init_venv] {interpreter=}")
|
114
|
+
await execute_command(
|
115
|
+
cwd=path,
|
116
|
+
command=f"{interpreter} -m venv venv",
|
117
|
+
logger_name=logger_name,
|
118
|
+
)
|
119
|
+
python_bin = path / "venv/bin/python"
|
120
|
+
logger.debug(f"[_init_venv] {python_bin=}")
|
121
|
+
return python_bin
|
@@ -3,11 +3,11 @@ from typing import Optional
|
|
3
3
|
|
4
4
|
from pydantic import root_validator
|
5
5
|
|
6
|
-
from fractal_server.app.schemas import ManifestV1
|
7
|
-
from fractal_server.app.schemas import
|
6
|
+
from fractal_server.app.schemas.v1 import ManifestV1
|
7
|
+
from fractal_server.app.schemas.v1 import TaskCollectPipV1
|
8
8
|
|
9
9
|
|
10
|
-
class _TaskCollectPip(
|
10
|
+
class _TaskCollectPip(TaskCollectPipV1):
|
11
11
|
"""
|
12
12
|
Internal TaskCollectPip schema
|
13
13
|
|
@@ -5,61 +5,27 @@ is used as a background task for the task-collection endpoint.
|
|
5
5
|
import json
|
6
6
|
from pathlib import Path
|
7
7
|
from shutil import rmtree as shell_rmtree
|
8
|
-
from typing import Optional
|
9
8
|
|
9
|
+
from ..utils import _init_venv
|
10
|
+
from ..utils import _normalize_package_name
|
11
|
+
from ..utils import get_collection_log
|
12
|
+
from ..utils import get_collection_path
|
13
|
+
from ..utils import get_log_path
|
14
|
+
from ..utils import slugify_task_name
|
15
|
+
from ._TaskCollectPip import _TaskCollectPip
|
10
16
|
from fractal_server.app.db import DBSyncSession
|
11
17
|
from fractal_server.app.db import get_sync_db
|
12
18
|
from fractal_server.app.models import State
|
13
19
|
from fractal_server.app.models import Task
|
14
|
-
from fractal_server.app.schemas import
|
15
|
-
from fractal_server.app.schemas import
|
16
|
-
from fractal_server.app.schemas import
|
20
|
+
from fractal_server.app.schemas.v1 import TaskCollectStatusV1
|
21
|
+
from fractal_server.app.schemas.v1 import TaskCreateV1
|
22
|
+
from fractal_server.app.schemas.v1 import TaskReadV1
|
17
23
|
from fractal_server.logger import close_logger
|
18
24
|
from fractal_server.logger import get_logger
|
19
25
|
from fractal_server.logger import set_logger
|
20
|
-
from fractal_server.tasks._TaskCollectPip import _TaskCollectPip
|
21
|
-
from fractal_server.tasks.utils import _normalize_package_name
|
22
|
-
from fractal_server.tasks.utils import get_collection_log
|
23
|
-
from fractal_server.tasks.utils import get_collection_path
|
24
|
-
from fractal_server.tasks.utils import get_log_path
|
25
|
-
from fractal_server.tasks.utils import get_python_interpreter
|
26
|
-
from fractal_server.tasks.utils import slugify_task_name
|
27
26
|
from fractal_server.utils import execute_command
|
28
27
|
|
29
28
|
|
30
|
-
async def _init_venv(
|
31
|
-
*,
|
32
|
-
path: Path,
|
33
|
-
python_version: Optional[str] = None,
|
34
|
-
logger_name: str,
|
35
|
-
) -> Path:
|
36
|
-
"""
|
37
|
-
Set a virtual environment at `path/venv`
|
38
|
-
|
39
|
-
Args:
|
40
|
-
path : Path
|
41
|
-
path to directory in which to set up the virtual environment
|
42
|
-
python_version : default=None
|
43
|
-
Python version the virtual environment will be based upon
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
python_bin : Path
|
47
|
-
path to python interpreter
|
48
|
-
"""
|
49
|
-
logger = get_logger(logger_name)
|
50
|
-
logger.debug(f"[_init_venv] {path=}")
|
51
|
-
interpreter = get_python_interpreter(version=python_version)
|
52
|
-
logger.debug(f"[_init_venv] {interpreter=}")
|
53
|
-
await execute_command(
|
54
|
-
cwd=path,
|
55
|
-
command=f"{interpreter} -m venv venv",
|
56
|
-
logger_name=logger_name,
|
57
|
-
)
|
58
|
-
python_bin = path / "venv/bin/python"
|
59
|
-
logger.debug(f"[_init_venv] {python_bin=}")
|
60
|
-
return python_bin
|
61
|
-
|
62
|
-
|
63
29
|
async def _pip_install(
|
64
30
|
venv_path: Path,
|
65
31
|
task_pkg: _TaskCollectPip,
|
@@ -218,7 +184,7 @@ async def create_package_environment_pip(
|
|
218
184
|
task_pkg: _TaskCollectPip,
|
219
185
|
venv_path: Path,
|
220
186
|
logger_name: str,
|
221
|
-
) -> list[
|
187
|
+
) -> list[TaskCreateV1]:
|
222
188
|
"""
|
223
189
|
Create environment, install package, and prepare task list
|
224
190
|
"""
|
@@ -263,7 +229,7 @@ async def create_package_environment_pip(
|
|
263
229
|
)
|
264
230
|
else:
|
265
231
|
additional_attrs = {}
|
266
|
-
this_task =
|
232
|
+
this_task = TaskCreateV1(
|
267
233
|
**t.dict(),
|
268
234
|
command=cmd,
|
269
235
|
version=task_pkg.package_version,
|
@@ -279,7 +245,7 @@ async def create_package_environment_pip(
|
|
279
245
|
|
280
246
|
|
281
247
|
async def _insert_tasks(
|
282
|
-
task_list: list[
|
248
|
+
task_list: list[TaskCreateV1],
|
283
249
|
db: DBSyncSession,
|
284
250
|
) -> list[Task]:
|
285
251
|
"""
|
@@ -319,7 +285,7 @@ async def background_collect_pip(
|
|
319
285
|
|
320
286
|
with next(get_sync_db()) as db:
|
321
287
|
state: State = db.get(State, state_id)
|
322
|
-
data =
|
288
|
+
data = TaskCollectStatusV1(**state.data)
|
323
289
|
data.info = None
|
324
290
|
|
325
291
|
try:
|
@@ -347,9 +313,11 @@ async def background_collect_pip(
|
|
347
313
|
# finalise
|
348
314
|
logger.debug("Task-collection status: finalising")
|
349
315
|
collection_path = get_collection_path(venv_path)
|
350
|
-
data.task_list = [
|
316
|
+
data.task_list = [
|
317
|
+
TaskReadV1(**task.model_dump()) for task in tasks
|
318
|
+
]
|
351
319
|
with collection_path.open("w") as f:
|
352
|
-
json.dump(data.sanitised_dict(), f)
|
320
|
+
json.dump(data.sanitised_dict(), f, indent=2)
|
353
321
|
|
354
322
|
# Update DB
|
355
323
|
data.status = "OK"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import json
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from fractal_server.app.schemas.v1 import TaskCollectStatusV1
|
5
|
+
from fractal_server.tasks.utils import get_absolute_venv_path
|
6
|
+
from fractal_server.tasks.utils import get_collection_path
|
7
|
+
|
8
|
+
|
9
|
+
def get_collection_data(venv_path: Path) -> TaskCollectStatusV1:
|
10
|
+
package_path = get_absolute_venv_path(venv_path)
|
11
|
+
collection_path = get_collection_path(package_path)
|
12
|
+
with collection_path.open() as f:
|
13
|
+
data = json.load(f)
|
14
|
+
return TaskCollectStatusV1(**data)
|
@@ -0,0 +1,103 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from pydantic import root_validator
|
5
|
+
|
6
|
+
from fractal_server.app.schemas.v2 import ManifestV2
|
7
|
+
from fractal_server.app.schemas.v2 import TaskCollectPipV2
|
8
|
+
|
9
|
+
|
10
|
+
class _TaskCollectPip(TaskCollectPipV2):
|
11
|
+
"""
|
12
|
+
Internal TaskCollectPip schema
|
13
|
+
|
14
|
+
Differences with its parent class (`TaskCollectPip`):
|
15
|
+
|
16
|
+
1. We check if the package corresponds to a path in the filesystem, and
|
17
|
+
whether it exists (via new validator `check_local_package`, new
|
18
|
+
method `is_local_package` and new attribute `package_path`).
|
19
|
+
2. We include an additional `package_manifest` attribute.
|
20
|
+
3. We expose an additional attribute `package_name`, which is filled
|
21
|
+
during task collection.
|
22
|
+
"""
|
23
|
+
|
24
|
+
package_name: Optional[str] = None
|
25
|
+
package_path: Optional[Path] = None
|
26
|
+
package_manifest: Optional[ManifestV2] = None
|
27
|
+
|
28
|
+
@property
|
29
|
+
def is_local_package(self) -> bool:
|
30
|
+
return bool(self.package_path)
|
31
|
+
|
32
|
+
@root_validator(pre=True)
|
33
|
+
def check_local_package(cls, values):
|
34
|
+
"""
|
35
|
+
Checks if package corresponds to an existing path on the filesystem
|
36
|
+
|
37
|
+
In this case, the user is providing directly a package file, rather
|
38
|
+
than a remote one from PyPI. We set the `package_path` attribute and
|
39
|
+
get the actual package name and version from the package file name.
|
40
|
+
"""
|
41
|
+
if "/" in values["package"]:
|
42
|
+
package_path = Path(values["package"])
|
43
|
+
if not package_path.is_absolute():
|
44
|
+
raise ValueError("Package path must be absolute")
|
45
|
+
if package_path.exists():
|
46
|
+
values["package_path"] = package_path
|
47
|
+
(
|
48
|
+
values["package"],
|
49
|
+
values["version"],
|
50
|
+
*_,
|
51
|
+
) = package_path.name.split("-")
|
52
|
+
else:
|
53
|
+
raise ValueError(f"Package {package_path} does not exist.")
|
54
|
+
return values
|
55
|
+
|
56
|
+
@property
|
57
|
+
def package_source(self) -> str:
|
58
|
+
"""
|
59
|
+
NOTE: As of PR #1188 in `fractal-server`, the attribute
|
60
|
+
`self.package_name` is normalized; this means e.g. that `_` is
|
61
|
+
replaced by `-`. To guarantee backwards compatibility with
|
62
|
+
`Task.source` attributes created before this change, we still replace
|
63
|
+
`-` with `_` upon generation of the `source` attribute, in this
|
64
|
+
method.
|
65
|
+
"""
|
66
|
+
if not self.package_name or not self.package_version:
|
67
|
+
raise ValueError(
|
68
|
+
"Cannot construct `package_source` property with "
|
69
|
+
f"{self.package_name=} and {self.package_version=}."
|
70
|
+
)
|
71
|
+
if self.is_local_package:
|
72
|
+
collection_type = "pip_local"
|
73
|
+
else:
|
74
|
+
collection_type = "pip_remote"
|
75
|
+
|
76
|
+
package_extras = self.package_extras or ""
|
77
|
+
if self.python_version:
|
78
|
+
python_version = f"py{self.python_version}"
|
79
|
+
else:
|
80
|
+
python_version = "" # FIXME: can we allow this?
|
81
|
+
|
82
|
+
source = ":".join(
|
83
|
+
(
|
84
|
+
collection_type,
|
85
|
+
self.package_name.replace("-", "_"), # see method docstring
|
86
|
+
self.package_version,
|
87
|
+
package_extras,
|
88
|
+
python_version,
|
89
|
+
)
|
90
|
+
)
|
91
|
+
return source
|
92
|
+
|
93
|
+
def check(self):
|
94
|
+
"""
|
95
|
+
Verify that the package has all attributes that are needed to continue
|
96
|
+
with task collection
|
97
|
+
"""
|
98
|
+
if not self.package_name:
|
99
|
+
raise ValueError("`package_name` attribute is not set")
|
100
|
+
if not self.package_version:
|
101
|
+
raise ValueError("`package_version` attribute is not set")
|
102
|
+
if not self.package_manifest:
|
103
|
+
raise ValueError("`package_manifest` attribute is not set")
|