fractal-server 1.3.0a2__py3-none-any.whl → 1.3.0a3__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/api/v1/dataset.py +21 -0
- fractal_server/app/api/v1/task.py +78 -16
- fractal_server/app/api/v1/workflow.py +36 -25
- fractal_server/app/models/job.py +3 -2
- fractal_server/app/models/project.py +4 -3
- fractal_server/app/models/security.py +2 -0
- fractal_server/app/models/state.py +2 -1
- fractal_server/app/models/task.py +20 -9
- fractal_server/app/models/workflow.py +34 -29
- fractal_server/app/runner/_common.py +13 -12
- fractal_server/app/runner/_slurm/executor.py +2 -1
- fractal_server/common/requirements.txt +1 -1
- fractal_server/common/schemas/__init__.py +2 -0
- fractal_server/common/schemas/applyworkflow.py +6 -7
- fractal_server/common/schemas/manifest.py +32 -15
- fractal_server/common/schemas/project.py +8 -10
- fractal_server/common/schemas/state.py +3 -4
- fractal_server/common/schemas/task.py +28 -97
- fractal_server/common/schemas/task_collection.py +101 -0
- fractal_server/common/schemas/user.py +5 -0
- fractal_server/common/schemas/workflow.py +9 -11
- fractal_server/common/tests/test_manifest.py +36 -4
- fractal_server/common/tests/test_task.py +16 -0
- fractal_server/common/tests/test_task_collection.py +24 -0
- fractal_server/common/tests/test_user.py +12 -0
- fractal_server/main.py +3 -0
- fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +38 -0
- fractal_server/migrations/versions/{e8f4051440be_new_initial_schema.py → 50a13d6138fd_initial_schema.py} +18 -10
- fractal_server/migrations/versions/{fda995215ae9_drop_applyworkflow_overwrite_input.py → f384e1c0cf5d_drop_task_default_args_columns.py} +9 -10
- fractal_server/tasks/collection.py +180 -115
- {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/METADATA +2 -1
- {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/RECORD +36 -34
- {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/WHEEL +1 -1
- fractal_server/migrations/versions/bb1cca2acc40_add_applyworkflow_end_timestamp.py +0 -31
- {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/LICENSE +0 -0
- {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/entry_points.txt +0 -0
@@ -1,20 +1,19 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
from typing import List
|
3
2
|
from typing import Optional
|
4
3
|
|
4
|
+
from pydantic import BaseModel
|
5
5
|
from pydantic import validator
|
6
|
-
from sqlmodel import SQLModel
|
7
6
|
|
8
7
|
from ._validators import valstr
|
9
8
|
|
10
9
|
__all__ = (
|
11
|
-
"
|
10
|
+
"_ApplyWorkflowBase",
|
12
11
|
"ApplyWorkflowCreate",
|
13
12
|
"ApplyWorkflowRead",
|
14
13
|
)
|
15
14
|
|
16
15
|
|
17
|
-
class
|
16
|
+
class _ApplyWorkflowBase(BaseModel):
|
18
17
|
"""
|
19
18
|
Base class for ApplyWorkflow
|
20
19
|
|
@@ -25,7 +24,7 @@ class ApplyWorkflowBase(SQLModel):
|
|
25
24
|
worker_init: Optional[str]
|
26
25
|
|
27
26
|
|
28
|
-
class ApplyWorkflowCreate(
|
27
|
+
class ApplyWorkflowCreate(_ApplyWorkflowBase):
|
29
28
|
|
30
29
|
# Validators
|
31
30
|
_worker_init = validator("worker_init", allow_reuse=True)(
|
@@ -33,7 +32,7 @@ class ApplyWorkflowCreate(ApplyWorkflowBase):
|
|
33
32
|
)
|
34
33
|
|
35
34
|
|
36
|
-
class ApplyWorkflowRead(
|
35
|
+
class ApplyWorkflowRead(_ApplyWorkflowBase):
|
37
36
|
id: int
|
38
37
|
project_id: int
|
39
38
|
workflow_id: int
|
@@ -43,7 +42,7 @@ class ApplyWorkflowRead(ApplyWorkflowBase):
|
|
43
42
|
end_timestamp: Optional[datetime]
|
44
43
|
status: str
|
45
44
|
log: Optional[str]
|
46
|
-
history: Optional[
|
45
|
+
history: Optional[list[str]]
|
47
46
|
working_dir: Optional[str]
|
48
47
|
working_dir_user: Optional[str]
|
49
48
|
|
@@ -1,13 +1,11 @@
|
|
1
|
-
from pathlib import Path
|
2
1
|
from typing import Any
|
3
|
-
from typing import Dict
|
4
|
-
from typing import List
|
5
2
|
from typing import Optional
|
6
3
|
from typing import TypeVar
|
7
4
|
|
8
5
|
from pydantic import BaseModel
|
6
|
+
from pydantic import Field
|
7
|
+
from pydantic import root_validator
|
9
8
|
from pydantic import validator
|
10
|
-
from sqlmodel import Field
|
11
9
|
|
12
10
|
|
13
11
|
__all__ = ("TaskManifestV1", "ManifestV1")
|
@@ -15,7 +13,7 @@ __all__ = ("TaskManifestV1", "ManifestV1")
|
|
15
13
|
|
16
14
|
class _TaskManifestBase(BaseModel):
|
17
15
|
"""
|
18
|
-
Base class for TaskManifest
|
16
|
+
Base class for `TaskManifest`
|
19
17
|
|
20
18
|
Represents a task within a manfest
|
21
19
|
|
@@ -33,21 +31,20 @@ class _TaskManifestBase(BaseModel):
|
|
33
31
|
The input type accepted by the task
|
34
32
|
output_type:
|
35
33
|
The output type returned by the task
|
36
|
-
default_args:
|
37
|
-
An arbitrary, JSON-serializable dictionary containing the default
|
38
|
-
parameters that will be passed to the task
|
39
34
|
meta:
|
40
35
|
Additional information about the package, such as hash of the
|
41
36
|
executable, specific runtime requirements (e.g., need_gpu=True),
|
42
37
|
etc.
|
38
|
+
args_schema:
|
39
|
+
JSON Schema for task arguments
|
43
40
|
"""
|
44
41
|
|
45
42
|
name: str
|
46
|
-
executable:
|
43
|
+
executable: str
|
47
44
|
input_type: str
|
48
45
|
output_type: str
|
49
|
-
|
50
|
-
|
46
|
+
meta: Optional[dict[str, Any]] = Field(default_factory=dict)
|
47
|
+
args_schema: Optional[dict[str, Any]]
|
51
48
|
|
52
49
|
|
53
50
|
TaskManifestType = TypeVar("TaskManifestType", bound=_TaskManifestBase)
|
@@ -69,13 +66,33 @@ class _ManifestBase(BaseModel):
|
|
69
66
|
manifests as the schema evolves. This is for instance used by
|
70
67
|
Fractal to determine which subclass of the present base class needs
|
71
68
|
be used to read and validate the input.
|
72
|
-
task_list :
|
69
|
+
task_list : list[TaskManifestType]
|
73
70
|
The list of tasks, represented as specified by subclasses of the
|
74
71
|
_TaskManifestBase (a.k.a. TaskManifestType)
|
72
|
+
has_args_schemas:
|
73
|
+
`True` if the manifest incldues JSON Schemas for the arguments of
|
74
|
+
each task.
|
75
|
+
args_schema_version:
|
76
|
+
Label of how `args_schema`s were generated (e.g. `pydantic_v1`).
|
75
77
|
"""
|
76
78
|
|
77
79
|
manifest_version: str
|
78
|
-
task_list:
|
80
|
+
task_list: list[TaskManifestType]
|
81
|
+
has_args_schemas: bool = False
|
82
|
+
args_schema_version: Optional[str]
|
83
|
+
|
84
|
+
@root_validator()
|
85
|
+
def _check_args_schemas_are_present(cls, values):
|
86
|
+
has_args_schemas = values["has_args_schemas"]
|
87
|
+
task_list = values["task_list"]
|
88
|
+
if has_args_schemas:
|
89
|
+
for task in task_list:
|
90
|
+
if task.args_schema is None:
|
91
|
+
raise ValueError(
|
92
|
+
f'has_args_schemas={has_args_schemas} but task "'
|
93
|
+
f'{task.name}" has args_schema={task.args_schema}.'
|
94
|
+
)
|
95
|
+
return values
|
79
96
|
|
80
97
|
|
81
98
|
class TaskManifestV1(_TaskManifestBase):
|
@@ -87,9 +104,9 @@ class ManifestV1(_ManifestBase):
|
|
87
104
|
Manifest schema version 1
|
88
105
|
"""
|
89
106
|
|
90
|
-
task_list:
|
107
|
+
task_list: list[TaskManifestV1]
|
91
108
|
|
92
109
|
@validator("manifest_version")
|
93
110
|
def manifest_version_1(cls, value):
|
94
111
|
if value != "1":
|
95
|
-
raise ValueError("Wrong manifest version")
|
112
|
+
raise ValueError(f"Wrong manifest version (given {value})")
|
@@ -1,11 +1,9 @@
|
|
1
1
|
from typing import Any
|
2
|
-
from typing import Dict
|
3
|
-
from typing import List
|
4
2
|
from typing import Optional
|
5
3
|
|
4
|
+
from pydantic import BaseModel
|
6
5
|
from pydantic import Field
|
7
6
|
from pydantic import validator
|
8
|
-
from sqlmodel import SQLModel
|
9
7
|
|
10
8
|
from ._validators import val_absolute_path
|
11
9
|
from ._validators import valstr
|
@@ -27,7 +25,7 @@ __all__ = (
|
|
27
25
|
# RESOURCE
|
28
26
|
|
29
27
|
|
30
|
-
class _ResourceBase(
|
28
|
+
class _ResourceBase(BaseModel):
|
31
29
|
"""
|
32
30
|
Base class for Resource
|
33
31
|
"""
|
@@ -53,7 +51,7 @@ class ResourceRead(_ResourceBase):
|
|
53
51
|
# DATASET
|
54
52
|
|
55
53
|
|
56
|
-
class _DatasetBase(
|
54
|
+
class _DatasetBase(BaseModel):
|
57
55
|
"""
|
58
56
|
Base class for Dataset
|
59
57
|
|
@@ -66,13 +64,13 @@ class _DatasetBase(SQLModel):
|
|
66
64
|
|
67
65
|
name: str
|
68
66
|
type: Optional[str]
|
69
|
-
meta:
|
67
|
+
meta: dict[str, Any] = Field(default={})
|
70
68
|
read_only: bool = False
|
71
69
|
|
72
70
|
|
73
71
|
class DatasetUpdate(_DatasetBase):
|
74
72
|
name: Optional[str]
|
75
|
-
meta: Optional[
|
73
|
+
meta: Optional[dict[str, Any]] = None
|
76
74
|
read_only: Optional[bool]
|
77
75
|
|
78
76
|
# Validators
|
@@ -88,7 +86,7 @@ class DatasetCreate(_DatasetBase):
|
|
88
86
|
|
89
87
|
class DatasetRead(_DatasetBase):
|
90
88
|
id: int
|
91
|
-
resource_list:
|
89
|
+
resource_list: list[ResourceRead]
|
92
90
|
project_id: int
|
93
91
|
read_only: bool
|
94
92
|
|
@@ -96,7 +94,7 @@ class DatasetRead(_DatasetBase):
|
|
96
94
|
# PROJECT
|
97
95
|
|
98
96
|
|
99
|
-
class _ProjectBase(
|
97
|
+
class _ProjectBase(BaseModel):
|
100
98
|
"""
|
101
99
|
Base class for Project
|
102
100
|
|
@@ -121,7 +119,7 @@ class ProjectCreate(_ProjectBase):
|
|
121
119
|
|
122
120
|
class ProjectRead(_ProjectBase):
|
123
121
|
id: int
|
124
|
-
dataset_list:
|
122
|
+
dataset_list: list[DatasetRead] = []
|
125
123
|
|
126
124
|
|
127
125
|
class ProjectUpdate(_ProjectBase):
|
@@ -1,9 +1,8 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Any
|
3
|
-
from typing import Dict
|
4
3
|
from typing import Optional
|
5
4
|
|
6
|
-
from
|
5
|
+
from pydantic import BaseModel
|
7
6
|
|
8
7
|
|
9
8
|
__all__ = (
|
@@ -12,7 +11,7 @@ __all__ = (
|
|
12
11
|
)
|
13
12
|
|
14
13
|
|
15
|
-
class _StateBase(
|
14
|
+
class _StateBase(BaseModel):
|
16
15
|
"""
|
17
16
|
Base class for `State`
|
18
17
|
|
@@ -22,7 +21,7 @@ class _StateBase(SQLModel):
|
|
22
21
|
timestamp: Time stamp of the state
|
23
22
|
"""
|
24
23
|
|
25
|
-
data:
|
24
|
+
data: dict[str, Any]
|
26
25
|
timestamp: datetime
|
27
26
|
|
28
27
|
def sanitised_dict(self):
|
@@ -1,14 +1,9 @@
|
|
1
|
-
from pathlib import Path
|
2
1
|
from typing import Any
|
3
|
-
from typing import Dict
|
4
|
-
from typing import List
|
5
|
-
from typing import Literal
|
6
2
|
from typing import Optional
|
7
3
|
|
8
4
|
from pydantic import BaseModel
|
5
|
+
from pydantic import Field
|
9
6
|
from pydantic import validator
|
10
|
-
from sqlmodel import Field
|
11
|
-
from sqlmodel import SQLModel
|
12
7
|
|
13
8
|
from ._validators import valstr
|
14
9
|
|
@@ -18,37 +13,22 @@ __all__ = (
|
|
18
13
|
"TaskRead",
|
19
14
|
"TaskImport",
|
20
15
|
"TaskExport",
|
21
|
-
"TaskCollectPip",
|
22
|
-
"TaskCollectStatus",
|
23
16
|
)
|
24
17
|
|
25
18
|
|
26
|
-
class _TaskBase(
|
27
|
-
"""
|
28
|
-
Task base class
|
19
|
+
class _TaskBase(BaseModel):
|
20
|
+
"""
|
29
21
|
|
30
|
-
|
31
|
-
executable.
|
22
|
+
Base class for `Task` and `Task{Create,Read,Update,Import,Export}` models
|
32
23
|
|
33
24
|
Attributes
|
34
|
-
name:
|
35
|
-
A human readable name for the task
|
36
|
-
command:
|
37
|
-
The command(s) that executes the task
|
38
25
|
source:
|
39
|
-
|
40
|
-
|
41
|
-
imported.
|
42
|
-
input_type:
|
43
|
-
The type of data the task expects as input
|
44
|
-
output_type:
|
45
|
-
The type of data the task expects as output
|
46
|
-
default_args: Optional[Dict[str, Any]]
|
47
|
-
Dictionary (saved as JSON) of the default parameters of the task
|
26
|
+
This is the information is used to match tasks across fractal
|
27
|
+
installations when a workflow is imported.
|
48
28
|
"""
|
49
29
|
|
50
|
-
name: str
|
51
30
|
source: str
|
31
|
+
_source = validator("source", allow_reuse=True)(valstr("source"))
|
52
32
|
|
53
33
|
|
54
34
|
class TaskUpdate(_TaskBase):
|
@@ -57,8 +37,10 @@ class TaskUpdate(_TaskBase):
|
|
57
37
|
output_type: Optional[str]
|
58
38
|
command: Optional[str]
|
59
39
|
source: Optional[str]
|
60
|
-
|
61
|
-
|
40
|
+
meta: Optional[dict[str, Any]]
|
41
|
+
version: Optional[str]
|
42
|
+
args_schema: Optional[dict[str, Any]]
|
43
|
+
args_schema_version: Optional[str]
|
62
44
|
|
63
45
|
# Validators
|
64
46
|
_name = validator("name", allow_reuse=True)(valstr("name"))
|
@@ -69,12 +51,11 @@ class TaskUpdate(_TaskBase):
|
|
69
51
|
valstr("output_type")
|
70
52
|
)
|
71
53
|
_command = validator("command", allow_reuse=True)(valstr("command"))
|
72
|
-
|
54
|
+
_version = validator("version", allow_reuse=True)(valstr("version"))
|
73
55
|
|
74
56
|
|
75
57
|
class TaskImport(_TaskBase):
|
76
|
-
|
77
|
-
_source = validator("source", allow_reuse=True)(valstr("source"))
|
58
|
+
pass
|
78
59
|
|
79
60
|
|
80
61
|
class TaskExport(_TaskBase):
|
@@ -83,19 +64,26 @@ class TaskExport(_TaskBase):
|
|
83
64
|
|
84
65
|
class TaskRead(_TaskBase):
|
85
66
|
id: int
|
67
|
+
name: str
|
86
68
|
command: str
|
87
69
|
input_type: str
|
88
70
|
output_type: str
|
89
|
-
|
90
|
-
|
71
|
+
meta: Optional[dict[str, Any]] = Field(default={})
|
72
|
+
owner: Optional[str]
|
73
|
+
version: Optional[str]
|
74
|
+
args_schema: Optional[dict[str, Any]]
|
75
|
+
args_schema_version: Optional[str]
|
91
76
|
|
92
77
|
|
93
78
|
class TaskCreate(_TaskBase):
|
79
|
+
name: str
|
94
80
|
command: str
|
95
81
|
input_type: str
|
96
82
|
output_type: str
|
97
|
-
|
98
|
-
|
83
|
+
meta: Optional[dict[str, Any]] = Field(default={})
|
84
|
+
version: Optional[str]
|
85
|
+
args_schema: Optional[dict[str, Any]]
|
86
|
+
args_schema_version: Optional[str]
|
99
87
|
|
100
88
|
# Validators
|
101
89
|
_name = validator("name", allow_reuse=True)(valstr("name"))
|
@@ -106,64 +94,7 @@ class TaskCreate(_TaskBase):
|
|
106
94
|
valstr("output_type")
|
107
95
|
)
|
108
96
|
_command = validator("command", allow_reuse=True)(valstr("command"))
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
pass
|
114
|
-
|
115
|
-
|
116
|
-
class TaskCollectPip(_TaskCollectBase):
|
117
|
-
"""
|
118
|
-
TaskCollectPip class
|
119
|
-
|
120
|
-
Attributes:
|
121
|
-
package: TBD
|
122
|
-
version: TBD
|
123
|
-
python_version: TBD
|
124
|
-
package_extras: TBD
|
125
|
-
"""
|
126
|
-
|
127
|
-
package: str
|
128
|
-
version: Optional[str]
|
129
|
-
python_version: Optional[str] = None
|
130
|
-
package_extras: Optional[str]
|
131
|
-
|
132
|
-
@validator("package")
|
133
|
-
def package_path_absolute(cls, value):
|
134
|
-
if "/" in value:
|
135
|
-
package_path = Path(value)
|
136
|
-
if not package_path.is_absolute():
|
137
|
-
raise ValueError(
|
138
|
-
f"Package path must be absolute: {package_path}"
|
139
|
-
)
|
140
|
-
return value
|
141
|
-
|
142
|
-
|
143
|
-
class TaskCollectStatus(_TaskCollectBase):
|
144
|
-
"""
|
145
|
-
TaskCollectStatus class
|
146
|
-
|
147
|
-
Attributes:
|
148
|
-
status: TBD
|
149
|
-
package: TBD
|
150
|
-
venv_path: TBD
|
151
|
-
task_list: TBD
|
152
|
-
log: TBD
|
153
|
-
info: TBD
|
154
|
-
"""
|
155
|
-
|
156
|
-
status: Literal["pending", "installing", "collecting", "fail", "OK"]
|
157
|
-
package: str
|
158
|
-
venv_path: Path
|
159
|
-
task_list: Optional[List[TaskRead]] = Field(default=[])
|
160
|
-
log: Optional[str]
|
161
|
-
info: Optional[str]
|
162
|
-
|
163
|
-
def sanitised_dict(self):
|
164
|
-
"""
|
165
|
-
Return `self.dict()` after casting `self.venv_path` to a string
|
166
|
-
"""
|
167
|
-
d = self.dict()
|
168
|
-
d["venv_path"] = str(self.venv_path)
|
169
|
-
return d
|
97
|
+
_version = validator("version", allow_reuse=True)(valstr("version"))
|
98
|
+
_args_schema_version = validator("args_schema_version", allow_reuse=True)(
|
99
|
+
valstr("args_schema_version")
|
100
|
+
)
|
@@ -0,0 +1,101 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Literal
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
6
|
+
from pydantic import Field
|
7
|
+
from pydantic import validator
|
8
|
+
|
9
|
+
from ._validators import valstr
|
10
|
+
from .task import TaskRead
|
11
|
+
|
12
|
+
__all__ = (
|
13
|
+
"TaskCollectPip",
|
14
|
+
"TaskCollectStatus",
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
class _TaskCollectBase(BaseModel):
|
19
|
+
pass
|
20
|
+
|
21
|
+
|
22
|
+
class TaskCollectPip(_TaskCollectBase):
|
23
|
+
"""
|
24
|
+
TaskCollectPip class
|
25
|
+
|
26
|
+
This class only encodes the attributes required to trigger a
|
27
|
+
task-collection operation. Other attributes (that are assigned *during*
|
28
|
+
task collection) are defined as part of fractal-server.
|
29
|
+
|
30
|
+
Two cases are supported:
|
31
|
+
|
32
|
+
1. `package` is the path of a local wheel file;
|
33
|
+
2. `package` is the name of a package that can be installed via `pip`.
|
34
|
+
|
35
|
+
|
36
|
+
Attributes:
|
37
|
+
package:
|
38
|
+
The name of a `pip`-installable package, or the path to a local
|
39
|
+
wheel file.
|
40
|
+
package_version: Version of the package
|
41
|
+
package_extras: Package extras to include in the `pip install` command
|
42
|
+
python_version: Python version to install and run the package tasks
|
43
|
+
"""
|
44
|
+
|
45
|
+
package: str
|
46
|
+
package_version: Optional[str] = None
|
47
|
+
package_extras: Optional[str] = None
|
48
|
+
python_version: Optional[str] = None
|
49
|
+
|
50
|
+
_package_version = validator("package_version", allow_reuse=True)(
|
51
|
+
valstr("package_version")
|
52
|
+
)
|
53
|
+
_package_extras = validator("package_extras", allow_reuse=True)(
|
54
|
+
valstr("package_extras")
|
55
|
+
)
|
56
|
+
_python_version = validator("python_version", allow_reuse=True)(
|
57
|
+
valstr("python_version")
|
58
|
+
)
|
59
|
+
|
60
|
+
@validator("package")
|
61
|
+
def package_validator(cls, value):
|
62
|
+
if "/" in value:
|
63
|
+
if not value.endswith(".whl"):
|
64
|
+
raise ValueError(
|
65
|
+
"Local-package path must be a wheel file "
|
66
|
+
f"(given {value})."
|
67
|
+
)
|
68
|
+
if not Path(value).is_absolute():
|
69
|
+
raise ValueError(
|
70
|
+
f"Local-package path must be absolute: (given {value})."
|
71
|
+
)
|
72
|
+
return value
|
73
|
+
|
74
|
+
|
75
|
+
class TaskCollectStatus(_TaskCollectBase):
|
76
|
+
"""
|
77
|
+
TaskCollectStatus class
|
78
|
+
|
79
|
+
Attributes:
|
80
|
+
status: TBD
|
81
|
+
package: TBD
|
82
|
+
venv_path: TBD
|
83
|
+
task_list: TBD
|
84
|
+
log: TBD
|
85
|
+
info: TBD
|
86
|
+
"""
|
87
|
+
|
88
|
+
status: Literal["pending", "installing", "collecting", "fail", "OK"]
|
89
|
+
package: str
|
90
|
+
venv_path: Path
|
91
|
+
task_list: Optional[list[TaskRead]] = Field(default=[])
|
92
|
+
log: Optional[str]
|
93
|
+
info: Optional[str]
|
94
|
+
|
95
|
+
def sanitised_dict(self):
|
96
|
+
"""
|
97
|
+
Return `self.dict()` after casting `self.venv_path` to a string
|
98
|
+
"""
|
99
|
+
d = self.dict()
|
100
|
+
d["venv_path"] = str(self.venv_path)
|
101
|
+
return d
|
@@ -17,16 +17,19 @@ __all__ = (
|
|
17
17
|
class UserRead(schemas.BaseUser[int]):
|
18
18
|
slurm_user: Optional[str]
|
19
19
|
cache_dir: Optional[str]
|
20
|
+
username: Optional[str]
|
20
21
|
|
21
22
|
|
22
23
|
class UserUpdate(schemas.BaseUserUpdate):
|
23
24
|
slurm_user: Optional[str]
|
24
25
|
cache_dir: Optional[str]
|
26
|
+
username: Optional[str]
|
25
27
|
|
26
28
|
# Validators
|
27
29
|
_slurm_user = validator("slurm_user", allow_reuse=True)(
|
28
30
|
valstr("slurm_user")
|
29
31
|
)
|
32
|
+
_username = validator("username", allow_reuse=True)(valstr("username"))
|
30
33
|
_cache_dir = validator("cache_dir", allow_reuse=True)(
|
31
34
|
val_absolute_path("cache_dir")
|
32
35
|
)
|
@@ -35,11 +38,13 @@ class UserUpdate(schemas.BaseUserUpdate):
|
|
35
38
|
class UserCreate(schemas.BaseUserCreate):
|
36
39
|
slurm_user: Optional[str]
|
37
40
|
cache_dir: Optional[str]
|
41
|
+
username: Optional[str]
|
38
42
|
|
39
43
|
# Validators
|
40
44
|
_slurm_user = validator("slurm_user", allow_reuse=True)(
|
41
45
|
valstr("slurm_user")
|
42
46
|
)
|
47
|
+
_username = validator("username", allow_reuse=True)(valstr("username"))
|
43
48
|
_cache_dir = validator("cache_dir", allow_reuse=True)(
|
44
49
|
val_absolute_path("cache_dir")
|
45
50
|
)
|
@@ -1,10 +1,8 @@
|
|
1
1
|
from typing import Any
|
2
|
-
from typing import Dict
|
3
|
-
from typing import List
|
4
2
|
from typing import Optional
|
5
3
|
|
4
|
+
from pydantic import BaseModel
|
6
5
|
from pydantic import validator
|
7
|
-
from sqlmodel import SQLModel
|
8
6
|
|
9
7
|
from ._validators import valint
|
10
8
|
from ._validators import valstr
|
@@ -27,10 +25,10 @@ __all__ = (
|
|
27
25
|
)
|
28
26
|
|
29
27
|
|
30
|
-
class _WorkflowTaskBase(
|
28
|
+
class _WorkflowTaskBase(BaseModel):
|
31
29
|
|
32
|
-
meta: Optional[
|
33
|
-
args: Optional[
|
30
|
+
meta: Optional[dict[str, Any]] = None
|
31
|
+
args: Optional[dict[str, Any]] = None
|
34
32
|
|
35
33
|
|
36
34
|
class WorkflowTaskCreate(_WorkflowTaskBase):
|
@@ -66,14 +64,14 @@ class WorkflowTaskUpdate(_WorkflowTaskBase):
|
|
66
64
|
return m
|
67
65
|
|
68
66
|
|
69
|
-
class _WorkflowBase(
|
67
|
+
class _WorkflowBase(BaseModel):
|
70
68
|
name: str
|
71
69
|
|
72
70
|
|
73
71
|
class WorkflowRead(_WorkflowBase):
|
74
72
|
id: int
|
75
73
|
project_id: int
|
76
|
-
task_list:
|
74
|
+
task_list: list[WorkflowTaskRead]
|
77
75
|
|
78
76
|
|
79
77
|
class WorkflowCreate(_WorkflowBase):
|
@@ -83,7 +81,7 @@ class WorkflowCreate(_WorkflowBase):
|
|
83
81
|
|
84
82
|
class WorkflowUpdate(_WorkflowBase):
|
85
83
|
name: Optional[str]
|
86
|
-
reordered_workflowtask_ids: Optional[
|
84
|
+
reordered_workflowtask_ids: Optional[list[int]]
|
87
85
|
|
88
86
|
# Validators
|
89
87
|
_name = validator("name", allow_reuse=True)(valstr("name"))
|
@@ -98,11 +96,11 @@ class WorkflowUpdate(_WorkflowBase):
|
|
98
96
|
|
99
97
|
|
100
98
|
class WorkflowImport(_WorkflowBase):
|
101
|
-
task_list:
|
99
|
+
task_list: list[WorkflowTaskImport]
|
102
100
|
|
103
101
|
# Validators
|
104
102
|
_name = validator("name", allow_reuse=True)(valstr("name"))
|
105
103
|
|
106
104
|
|
107
105
|
class WorkflowExport(_WorkflowBase):
|
108
|
-
task_list:
|
106
|
+
task_list: list[WorkflowTaskExport]
|
@@ -8,15 +8,47 @@ from schemas import TaskManifestV1
|
|
8
8
|
|
9
9
|
def test_ManifestV1():
|
10
10
|
|
11
|
-
|
12
|
-
name="
|
11
|
+
task_without_args_schema = TaskManifestV1(
|
12
|
+
name="Task A",
|
13
13
|
executable="executable",
|
14
14
|
input_type="input_type",
|
15
15
|
output_type="output_type",
|
16
16
|
default_args={"arg": "val"},
|
17
17
|
)
|
18
|
-
|
18
|
+
task_with_args_schema = TaskManifestV1(
|
19
|
+
name="Task B",
|
20
|
+
executable="executable",
|
21
|
+
input_type="input_type",
|
22
|
+
output_type="output_type",
|
23
|
+
default_args={"arg": "val"},
|
24
|
+
args_schema={"something": "else"},
|
25
|
+
)
|
26
|
+
|
27
|
+
m = ManifestV1(
|
28
|
+
manifest_version="1",
|
29
|
+
task_list=[task_without_args_schema],
|
30
|
+
)
|
31
|
+
debug(m)
|
32
|
+
m = ManifestV1(
|
33
|
+
manifest_version="1",
|
34
|
+
has_args_schemas=False,
|
35
|
+
task_list=[task_without_args_schema],
|
36
|
+
)
|
19
37
|
debug(m)
|
38
|
+
m = ManifestV1(
|
39
|
+
manifest_version="1",
|
40
|
+
has_args_schemas=True,
|
41
|
+
task_list=[task_with_args_schema],
|
42
|
+
)
|
43
|
+
debug(m)
|
44
|
+
|
45
|
+
with pytest.raises(ValidationError) as e:
|
46
|
+
m = ManifestV1(
|
47
|
+
manifest_version="1",
|
48
|
+
task_list=[task_without_args_schema, task_with_args_schema],
|
49
|
+
has_args_schemas=True,
|
50
|
+
)
|
51
|
+
debug(e.value)
|
20
52
|
|
21
53
|
with pytest.raises(ValidationError):
|
22
|
-
ManifestV1(manifest_version="2", task_list=[])
|
54
|
+
ManifestV1(manifest_version="2", task_list=[task_with_args_schema])
|