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.
Files changed (37) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/api/v1/dataset.py +21 -0
  3. fractal_server/app/api/v1/task.py +78 -16
  4. fractal_server/app/api/v1/workflow.py +36 -25
  5. fractal_server/app/models/job.py +3 -2
  6. fractal_server/app/models/project.py +4 -3
  7. fractal_server/app/models/security.py +2 -0
  8. fractal_server/app/models/state.py +2 -1
  9. fractal_server/app/models/task.py +20 -9
  10. fractal_server/app/models/workflow.py +34 -29
  11. fractal_server/app/runner/_common.py +13 -12
  12. fractal_server/app/runner/_slurm/executor.py +2 -1
  13. fractal_server/common/requirements.txt +1 -1
  14. fractal_server/common/schemas/__init__.py +2 -0
  15. fractal_server/common/schemas/applyworkflow.py +6 -7
  16. fractal_server/common/schemas/manifest.py +32 -15
  17. fractal_server/common/schemas/project.py +8 -10
  18. fractal_server/common/schemas/state.py +3 -4
  19. fractal_server/common/schemas/task.py +28 -97
  20. fractal_server/common/schemas/task_collection.py +101 -0
  21. fractal_server/common/schemas/user.py +5 -0
  22. fractal_server/common/schemas/workflow.py +9 -11
  23. fractal_server/common/tests/test_manifest.py +36 -4
  24. fractal_server/common/tests/test_task.py +16 -0
  25. fractal_server/common/tests/test_task_collection.py +24 -0
  26. fractal_server/common/tests/test_user.py +12 -0
  27. fractal_server/main.py +3 -0
  28. fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +38 -0
  29. fractal_server/migrations/versions/{e8f4051440be_new_initial_schema.py → 50a13d6138fd_initial_schema.py} +18 -10
  30. fractal_server/migrations/versions/{fda995215ae9_drop_applyworkflow_overwrite_input.py → f384e1c0cf5d_drop_task_default_args_columns.py} +9 -10
  31. fractal_server/tasks/collection.py +180 -115
  32. {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/METADATA +2 -1
  33. {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/RECORD +36 -34
  34. {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/WHEEL +1 -1
  35. fractal_server/migrations/versions/bb1cca2acc40_add_applyworkflow_end_timestamp.py +0 -31
  36. {fractal_server-1.3.0a2.dist-info → fractal_server-1.3.0a3.dist-info}/LICENSE +0 -0
  37. {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
- "ApplyWorkflowBase",
10
+ "_ApplyWorkflowBase",
12
11
  "ApplyWorkflowCreate",
13
12
  "ApplyWorkflowRead",
14
13
  )
15
14
 
16
15
 
17
- class ApplyWorkflowBase(SQLModel):
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(ApplyWorkflowBase):
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(ApplyWorkflowBase):
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[List[str]]
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: Path
43
+ executable: str
47
44
  input_type: str
48
45
  output_type: str
49
- default_args: Optional[Dict[str, Any]] = Field(default_factory=dict)
50
- meta: Optional[Dict[str, Any]] = Field(default_factory=dict)
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 : List[TaskManifestType]
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: List[TaskManifestType] # type: ignore
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: List[TaskManifestV1]
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(SQLModel):
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(SQLModel):
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: Dict[str, Any] = Field(default={})
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[Dict[str, Any]] = None
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: List[ResourceRead]
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(SQLModel):
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: List[DatasetRead] = []
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 sqlmodel import SQLModel
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(SQLModel):
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: Dict[str, Any]
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(SQLModel):
27
- """# TODO fix me
28
- Task base class
19
+ class _TaskBase(BaseModel):
20
+ """
29
21
 
30
- A Task is the elemental unit of a workflow, and must be a self-standing
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
- Path or url to task source. This is the information is used to
40
- match tasks across fractal installations when a workflow is
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
- default_args: Optional[Dict[str, Any]]
61
- meta: Optional[Dict[str, Any]]
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
- _source = validator("source", allow_reuse=True)(valstr("source"))
54
+ _version = validator("version", allow_reuse=True)(valstr("version"))
73
55
 
74
56
 
75
57
  class TaskImport(_TaskBase):
76
- _name = validator("name", allow_reuse=True)(valstr("name"))
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
- default_args: Optional[Dict[str, Any]] = Field(default={})
90
- meta: Optional[Dict[str, Any]] = Field(default={})
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
- default_args: Optional[Dict[str, Any]] = Field(default={})
98
- meta: Optional[Dict[str, Any]] = Field(default={})
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
- _source = validator("source", allow_reuse=True)(valstr("source"))
110
-
111
-
112
- class _TaskCollectBase(BaseModel):
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(SQLModel):
28
+ class _WorkflowTaskBase(BaseModel):
31
29
 
32
- meta: Optional[Dict[str, Any]] = None
33
- args: Optional[Dict[str, Any]] = None
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(SQLModel):
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: List[WorkflowTaskRead]
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[List[int]]
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: List[WorkflowTaskImport]
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: List[WorkflowTaskExport]
106
+ task_list: list[WorkflowTaskExport]
@@ -8,15 +8,47 @@ from schemas import TaskManifestV1
8
8
 
9
9
  def test_ManifestV1():
10
10
 
11
- t = TaskManifestV1(
12
- name="task",
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
- m = ManifestV1(manifest_version="1", task_list=[t])
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])