fractal-server 2.12.0a1__py3-none-any.whl → 2.13.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.
Files changed (82) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +17 -63
  3. fractal_server/app/models/security.py +9 -12
  4. fractal_server/app/models/v2/dataset.py +2 -2
  5. fractal_server/app/models/v2/job.py +11 -9
  6. fractal_server/app/models/v2/task.py +2 -3
  7. fractal_server/app/models/v2/task_group.py +6 -2
  8. fractal_server/app/models/v2/workflowtask.py +15 -8
  9. fractal_server/app/routes/admin/v2/task.py +1 -1
  10. fractal_server/app/routes/admin/v2/task_group.py +1 -1
  11. fractal_server/app/routes/api/v2/dataset.py +4 -4
  12. fractal_server/app/routes/api/v2/images.py +11 -23
  13. fractal_server/app/routes/api/v2/project.py +2 -2
  14. fractal_server/app/routes/api/v2/status.py +1 -1
  15. fractal_server/app/routes/api/v2/submit.py +8 -6
  16. fractal_server/app/routes/api/v2/task.py +4 -2
  17. fractal_server/app/routes/api/v2/task_collection.py +3 -2
  18. fractal_server/app/routes/api/v2/task_group.py +2 -2
  19. fractal_server/app/routes/api/v2/workflow.py +3 -3
  20. fractal_server/app/routes/api/v2/workflow_import.py +3 -3
  21. fractal_server/app/routes/api/v2/workflowtask.py +3 -1
  22. fractal_server/app/routes/auth/_aux_auth.py +4 -1
  23. fractal_server/app/routes/auth/current_user.py +3 -5
  24. fractal_server/app/routes/auth/group.py +1 -1
  25. fractal_server/app/routes/auth/users.py +2 -4
  26. fractal_server/app/routes/aux/_runner.py +1 -1
  27. fractal_server/app/routes/aux/validate_user_settings.py +1 -2
  28. fractal_server/app/runner/executors/_job_states.py +13 -0
  29. fractal_server/app/runner/executors/slurm/_slurm_config.py +26 -18
  30. fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -3
  31. fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +31 -22
  32. fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +2 -6
  33. fractal_server/app/runner/executors/slurm/ssh/executor.py +35 -50
  34. fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -3
  35. fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +1 -2
  36. fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +37 -47
  37. fractal_server/app/runner/executors/slurm/sudo/executor.py +77 -41
  38. fractal_server/app/runner/v2/__init__.py +0 -9
  39. fractal_server/app/runner/v2/_local/_local_config.py +5 -4
  40. fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +4 -4
  41. fractal_server/app/runner/v2/_slurm_sudo/__init__.py +2 -2
  42. fractal_server/app/runner/v2/deduplicate_list.py +1 -1
  43. fractal_server/app/runner/v2/runner.py +9 -4
  44. fractal_server/app/runner/v2/task_interface.py +15 -7
  45. fractal_server/app/schemas/_filter_validators.py +6 -3
  46. fractal_server/app/schemas/_validators.py +7 -5
  47. fractal_server/app/schemas/user.py +23 -18
  48. fractal_server/app/schemas/user_group.py +25 -11
  49. fractal_server/app/schemas/user_settings.py +31 -24
  50. fractal_server/app/schemas/v2/dataset.py +48 -35
  51. fractal_server/app/schemas/v2/dumps.py +16 -14
  52. fractal_server/app/schemas/v2/job.py +49 -29
  53. fractal_server/app/schemas/v2/manifest.py +32 -28
  54. fractal_server/app/schemas/v2/project.py +18 -8
  55. fractal_server/app/schemas/v2/task.py +86 -75
  56. fractal_server/app/schemas/v2/task_collection.py +41 -30
  57. fractal_server/app/schemas/v2/task_group.py +39 -20
  58. fractal_server/app/schemas/v2/workflow.py +24 -12
  59. fractal_server/app/schemas/v2/workflowtask.py +63 -61
  60. fractal_server/app/security/__init__.py +7 -4
  61. fractal_server/app/security/signup_email.py +21 -12
  62. fractal_server/config.py +123 -75
  63. fractal_server/images/models.py +18 -12
  64. fractal_server/main.py +13 -10
  65. fractal_server/migrations/env.py +16 -63
  66. fractal_server/tasks/v2/local/collect.py +9 -8
  67. fractal_server/tasks/v2/local/deactivate.py +3 -0
  68. fractal_server/tasks/v2/local/reactivate.py +3 -0
  69. fractal_server/tasks/v2/ssh/collect.py +8 -8
  70. fractal_server/tasks/v2/ssh/deactivate.py +3 -0
  71. fractal_server/tasks/v2/ssh/reactivate.py +9 -6
  72. fractal_server/tasks/v2/utils_background.py +1 -1
  73. fractal_server/tasks/v2/utils_database.py +1 -1
  74. {fractal_server-2.12.0a1.dist-info → fractal_server-2.13.0.dist-info}/METADATA +10 -11
  75. {fractal_server-2.12.0a1.dist-info → fractal_server-2.13.0.dist-info}/RECORD +78 -81
  76. fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -121
  77. fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -108
  78. fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -42
  79. fractal_server/app/runner/v2/_local_experimental/executor.py +0 -157
  80. {fractal_server-2.12.0a1.dist-info → fractal_server-2.13.0.dist-info}/LICENSE +0 -0
  81. {fractal_server-2.12.0a1.dist-info → fractal_server-2.13.0.dist-info}/WHEEL +0 -0
  82. {fractal_server-2.12.0a1.dist-info → fractal_server-2.13.0.dist-info}/entry_points.txt +0 -0
@@ -3,9 +3,9 @@ from typing import Optional
3
3
 
4
4
  from pydantic import BaseModel
5
5
  from pydantic import Field
6
+ from pydantic import field_validator
6
7
  from pydantic import HttpUrl
7
- from pydantic import root_validator
8
- from pydantic import validator
8
+ from pydantic import model_validator
9
9
 
10
10
  from .._validators import valstr
11
11
 
@@ -50,27 +50,24 @@ class TaskManifestV2(BaseModel):
50
50
  args_schema_non_parallel: Optional[dict[str, Any]] = None
51
51
  args_schema_parallel: Optional[dict[str, Any]] = None
52
52
  docs_info: Optional[str] = None
53
- docs_link: Optional[HttpUrl] = None
53
+ docs_link: Optional[str] = None
54
54
 
55
55
  category: Optional[str] = None
56
56
  modality: Optional[str] = None
57
57
  tags: list[str] = Field(default_factory=list)
58
58
 
59
- @root_validator
60
- def validate_executable_args_meta(cls, values):
61
-
62
- executable_non_parallel = values.get("executable_non_parallel")
63
- executable_parallel = values.get("executable_parallel")
59
+ @model_validator(mode="after")
60
+ def validate_executable_args_meta(self):
61
+ executable_non_parallel = self.executable_non_parallel
62
+ executable_parallel = self.executable_parallel
64
63
  if (executable_non_parallel is None) and (executable_parallel is None):
65
-
66
64
  raise ValueError(
67
65
  "`TaskManifestV2.executable_non_parallel` and "
68
66
  "`TaskManifestV2.executable_parallel` cannot be both None."
69
67
  )
70
68
 
71
69
  elif executable_non_parallel is None:
72
-
73
- meta_non_parallel = values.get("meta_non_parallel")
70
+ meta_non_parallel = self.meta_non_parallel
74
71
  if meta_non_parallel != {}:
75
72
  raise ValueError(
76
73
  "`TaskManifestV2.meta_non_parallel` must be an empty dict "
@@ -78,7 +75,7 @@ class TaskManifestV2(BaseModel):
78
75
  f"Given: {meta_non_parallel}."
79
76
  )
80
77
 
81
- args_schema_non_parallel = values.get("args_schema_non_parallel")
78
+ args_schema_non_parallel = self.args_schema_non_parallel
82
79
  if args_schema_non_parallel is not None:
83
80
  raise ValueError(
84
81
  "`TaskManifestV2.args_schema_non_parallel` must be None "
@@ -87,8 +84,7 @@ class TaskManifestV2(BaseModel):
87
84
  )
88
85
 
89
86
  elif executable_parallel is None:
90
-
91
- meta_parallel = values.get("meta_parallel")
87
+ meta_parallel = self.meta_parallel
92
88
  if meta_parallel != {}:
93
89
  raise ValueError(
94
90
  "`TaskManifestV2.meta_parallel` must be an empty dict if "
@@ -96,7 +92,7 @@ class TaskManifestV2(BaseModel):
96
92
  f"Given: {meta_parallel}."
97
93
  )
98
94
 
99
- args_schema_parallel = values.get("args_schema_parallel")
95
+ args_schema_parallel = self.args_schema_parallel
100
96
  if args_schema_parallel is not None:
101
97
  raise ValueError(
102
98
  "`TaskManifestV2.args_schema_parallel` must be None if "
@@ -104,7 +100,14 @@ class TaskManifestV2(BaseModel):
104
100
  f"Given: {args_schema_parallel}."
105
101
  )
106
102
 
107
- return values
103
+ return self
104
+
105
+ @field_validator("docs_link", mode="after")
106
+ @classmethod
107
+ def validate_docs_link(cls, value):
108
+ if value is not None:
109
+ HttpUrl(value)
110
+ return value
108
111
 
109
112
 
110
113
  class ManifestV2(BaseModel):
@@ -137,10 +140,10 @@ class ManifestV2(BaseModel):
137
140
  args_schema_version: Optional[str] = None
138
141
  authors: Optional[str] = None
139
142
 
140
- @root_validator()
141
- def _check_args_schemas_are_present(cls, values):
142
- has_args_schemas = values["has_args_schemas"]
143
- task_list = values["task_list"]
143
+ @model_validator(mode="after")
144
+ def _check_args_schemas_are_present(self):
145
+ has_args_schemas = self.has_args_schemas
146
+ task_list = self.task_list
144
147
  if has_args_schemas is True:
145
148
  for task in task_list:
146
149
  if task.executable_parallel is not None:
@@ -157,11 +160,11 @@ class ManifestV2(BaseModel):
157
160
  f"task '{task.name}' has "
158
161
  f"{task.args_schema_non_parallel=}."
159
162
  )
160
- return values
163
+ return self
161
164
 
162
- @root_validator()
163
- def _unique_task_names(cls, values):
164
- task_list = values["task_list"]
165
+ @model_validator(mode="after")
166
+ def _unique_task_names(self):
167
+ task_list = self.task_list
165
168
  task_list_names = [t.name for t in task_list]
166
169
  if len(set(task_list_names)) != len(task_list_names):
167
170
  raise ValueError(
@@ -170,14 +173,15 @@ class ManifestV2(BaseModel):
170
173
  f"Given: {task_list_names}.",
171
174
  )
172
175
  )
173
- return values
176
+ return self
174
177
 
175
- @validator("manifest_version")
178
+ @field_validator("manifest_version")
179
+ @classmethod
176
180
  def manifest_version_2(cls, value):
177
181
  if value != "2":
178
182
  raise ValueError(f"Wrong manifest version (given {value})")
179
183
  return value
180
184
 
181
- _authors = validator("authors", allow_reuse=True)(
182
- valstr("authors", accept_none=True)
185
+ _authors = field_validator("authors")(
186
+ classmethod(valstr("authors", accept_none=True))
183
187
  )
@@ -2,28 +2,38 @@ from datetime import datetime
2
2
  from typing import Optional
3
3
 
4
4
  from pydantic import BaseModel
5
- from pydantic import Extra
6
- from pydantic import validator
5
+ from pydantic import ConfigDict
6
+ from pydantic import field_serializer
7
+ from pydantic import field_validator
8
+ from pydantic.types import AwareDatetime
7
9
 
8
10
  from .._validators import valstr
9
11
 
10
12
 
11
- class ProjectCreateV2(BaseModel, extra=Extra.forbid):
13
+ class ProjectCreateV2(BaseModel):
14
+
15
+ model_config = ConfigDict(extra="forbid")
12
16
 
13
17
  name: str
14
18
  # Validators
15
- _name = validator("name", allow_reuse=True)(valstr("name"))
19
+ _name = field_validator("name")(classmethod(valstr("name")))
16
20
 
17
21
 
18
22
  class ProjectReadV2(BaseModel):
19
23
 
20
24
  id: int
21
25
  name: str
22
- timestamp_created: datetime
26
+ timestamp_created: AwareDatetime
27
+
28
+ @field_serializer("timestamp_created")
29
+ def serialize_datetime(v: datetime) -> str:
30
+ return v.isoformat()
31
+
23
32
 
33
+ class ProjectUpdateV2(BaseModel):
24
34
 
25
- class ProjectUpdateV2(BaseModel, extra=Extra.forbid):
35
+ model_config = ConfigDict(extra="forbid")
26
36
 
27
- name: Optional[str]
37
+ name: Optional[str] = None
28
38
  # Validators
29
- _name = validator("name", allow_reuse=True)(valstr("name"))
39
+ _name = field_validator("name")(classmethod(valstr("name")))
@@ -3,11 +3,11 @@ from typing import Literal
3
3
  from typing import Optional
4
4
 
5
5
  from pydantic import BaseModel
6
- from pydantic import Extra
6
+ from pydantic import ConfigDict
7
7
  from pydantic import Field
8
+ from pydantic import field_validator
8
9
  from pydantic import HttpUrl
9
- from pydantic import root_validator
10
- from pydantic import validator
10
+ from pydantic import model_validator
11
11
 
12
12
  from fractal_server.app.schemas._validators import val_unique_list
13
13
  from fractal_server.app.schemas._validators import valdict_keys
@@ -15,7 +15,8 @@ from fractal_server.app.schemas._validators import valstr
15
15
  from fractal_server.string_tools import validate_cmd
16
16
 
17
17
 
18
- class TaskCreateV2(BaseModel, extra=Extra.forbid):
18
+ class TaskCreateV2(BaseModel):
19
+ model_config = ConfigDict(extra="forbid")
19
20
 
20
21
  name: str
21
22
 
@@ -29,7 +30,7 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
29
30
  args_schema_parallel: Optional[dict[str, Any]] = None
30
31
  args_schema_version: Optional[str] = None
31
32
  docs_info: Optional[str] = None
32
- docs_link: Optional[HttpUrl] = None
33
+ docs_link: Optional[str] = None
33
34
 
34
35
  input_types: dict[str, bool] = Field(default={})
35
36
  output_types: dict[str, bool] = Field(default={})
@@ -40,10 +41,10 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
40
41
  authors: Optional[str] = None
41
42
 
42
43
  # Validators
43
- @root_validator
44
- def validate_commands(cls, values):
45
- command_parallel = values.get("command_parallel")
46
- command_non_parallel = values.get("command_non_parallel")
44
+ @model_validator(mode="after")
45
+ def validate_commands(self):
46
+ command_parallel = self.command_parallel
47
+ command_non_parallel = self.command_non_parallel
47
48
  if (command_parallel is None) and (command_non_parallel is None):
48
49
  raise ValueError(
49
50
  "Task must have at least one valid command "
@@ -54,58 +55,65 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
54
55
  if command_non_parallel is not None:
55
56
  validate_cmd(command_non_parallel)
56
57
 
57
- return values
58
+ return self
58
59
 
59
- _name = validator("name", allow_reuse=True)(valstr("name"))
60
- _command_non_parallel = validator(
61
- "command_non_parallel", allow_reuse=True
62
- )(valstr("command_non_parallel"))
63
- _command_parallel = validator("command_parallel", allow_reuse=True)(
64
- valstr("command_parallel")
60
+ _name = field_validator("name")(classmethod(valstr("name")))
61
+ _command_non_parallel = field_validator("command_non_parallel")(
62
+ classmethod(valstr("command_non_parallel"))
65
63
  )
66
- _version = validator("version", allow_reuse=True)(valstr("version"))
64
+ _command_parallel = field_validator("command_parallel")(
65
+ classmethod(valstr("command_parallel"))
66
+ )
67
+ _version = field_validator("version")(classmethod(valstr("version")))
67
68
 
68
- _meta_non_parallel = validator("meta_non_parallel", allow_reuse=True)(
69
- valdict_keys("meta_non_parallel")
69
+ _meta_non_parallel = field_validator("meta_non_parallel")(
70
+ classmethod(valdict_keys("meta_non_parallel"))
71
+ )
72
+ _meta_parallel = field_validator("meta_parallel")(
73
+ classmethod(valdict_keys("meta_parallel"))
70
74
  )
71
- _meta_parallel = validator("meta_parallel", allow_reuse=True)(
72
- valdict_keys("meta_parallel")
75
+ _args_schema_non_parallel = field_validator("args_schema_non_parallel")(
76
+ classmethod(valdict_keys("args_schema_non_parallel"))
73
77
  )
74
- _args_schema_non_parallel = validator(
75
- "args_schema_non_parallel", allow_reuse=True
76
- )(valdict_keys("args_schema_non_parallel"))
77
- _args_schema_parallel = validator(
78
- "args_schema_parallel", allow_reuse=True
79
- )(valdict_keys("args_schema_parallel"))
80
- _args_schema_version = validator("args_schema_version", allow_reuse=True)(
81
- valstr("args_schema_version")
78
+ _args_schema_parallel = field_validator("args_schema_parallel")(
79
+ classmethod(valdict_keys("args_schema_parallel"))
82
80
  )
83
- _input_types = validator("input_types", allow_reuse=True)(
84
- valdict_keys("input_types")
81
+ _args_schema_version = field_validator("args_schema_version")(
82
+ classmethod(valstr("args_schema_version"))
85
83
  )
86
- _output_types = validator("output_types", allow_reuse=True)(
87
- valdict_keys("output_types")
84
+ _input_types = field_validator("input_types")(
85
+ classmethod(valdict_keys("input_types"))
86
+ )
87
+ _output_types = field_validator("output_types")(
88
+ classmethod(valdict_keys("output_types"))
88
89
  )
89
90
 
90
- _category = validator("category", allow_reuse=True)(
91
- valstr("category", accept_none=True)
91
+ _category = field_validator("category")(
92
+ classmethod(valstr("category", accept_none=True))
92
93
  )
93
- _modality = validator("modality", allow_reuse=True)(
94
- valstr("modality", accept_none=True)
94
+ _modality = field_validator("modality")(
95
+ classmethod(valstr("modality", accept_none=True))
95
96
  )
96
- _authors = validator("authors", allow_reuse=True)(
97
- valstr("authors", accept_none=True)
97
+ _authors = field_validator("authors")(
98
+ classmethod(valstr("authors", accept_none=True))
98
99
  )
99
100
 
100
- @validator("tags")
101
+ @field_validator("tags")
102
+ @classmethod
101
103
  def validate_list_of_strings(cls, value):
102
104
  for i, tag in enumerate(value):
103
- value[i] = valstr(f"tags[{i}]")(tag)
104
- return val_unique_list("tags")(value)
105
+ value[i] = valstr(f"tags[{i}]")(cls, tag)
106
+ return val_unique_list("tags")(cls, value)
105
107
 
108
+ @field_validator("docs_link", mode="after")
109
+ @classmethod
110
+ def validate_docs_link(cls, value):
111
+ if value is not None:
112
+ HttpUrl(value)
113
+ return value
106
114
 
107
- class TaskReadV2(BaseModel):
108
115
 
116
+ class TaskReadV2(BaseModel):
109
117
  id: int
110
118
  name: str
111
119
  type: Literal["parallel", "non_parallel", "compound"]
@@ -120,7 +128,7 @@ class TaskReadV2(BaseModel):
120
128
  args_schema_parallel: Optional[dict[str, Any]] = None
121
129
  args_schema_version: Optional[str] = None
122
130
  docs_info: Optional[str] = None
123
- docs_link: Optional[HttpUrl] = None
131
+ docs_link: Optional[str] = None
124
132
  input_types: dict[str, bool]
125
133
  output_types: dict[str, bool]
126
134
 
@@ -132,7 +140,8 @@ class TaskReadV2(BaseModel):
132
140
  tags: list[str]
133
141
 
134
142
 
135
- class TaskUpdateV2(BaseModel, extra=Extra.forbid):
143
+ class TaskUpdateV2(BaseModel):
144
+ model_config = ConfigDict(extra="forbid")
136
145
 
137
146
  command_parallel: Optional[str] = None
138
147
  command_non_parallel: Optional[str] = None
@@ -145,67 +154,69 @@ class TaskUpdateV2(BaseModel, extra=Extra.forbid):
145
154
  tags: Optional[list[str]] = None
146
155
 
147
156
  # Validators
148
- @validator("input_types", "output_types")
157
+ @field_validator("input_types", "output_types")
158
+ @classmethod
149
159
  def val_is_dict(cls, v):
150
160
  if not isinstance(v, dict):
151
161
  raise ValueError
152
162
  return v
153
163
 
154
- _command_parallel = validator("command_parallel", allow_reuse=True)(
155
- valstr("command_parallel")
164
+ _command_parallel = field_validator("command_parallel")(
165
+ classmethod(valstr("command_parallel"))
166
+ )
167
+ _command_non_parallel = field_validator("command_non_parallel")(
168
+ classmethod(valstr("command_non_parallel"))
156
169
  )
157
- _command_non_parallel = validator(
158
- "command_non_parallel", allow_reuse=True
159
- )(valstr("command_non_parallel"))
160
- _input_types = validator("input_types", allow_reuse=True)(
161
- valdict_keys("input_types")
170
+ _input_types = field_validator("input_types")(
171
+ classmethod(valdict_keys("input_types"))
162
172
  )
163
- _output_types = validator("output_types", allow_reuse=True)(
164
- valdict_keys("output_types")
173
+ _output_types = field_validator("output_types")(
174
+ classmethod(valdict_keys("output_types"))
165
175
  )
166
176
 
167
- _category = validator("category", allow_reuse=True)(
168
- valstr("category", accept_none=True)
177
+ _category = field_validator("category")(
178
+ classmethod(valstr("category", accept_none=True))
169
179
  )
170
- _modality = validator("modality", allow_reuse=True)(
171
- valstr("modality", accept_none=True)
180
+ _modality = field_validator("modality")(
181
+ classmethod(valstr("modality", accept_none=True))
172
182
  )
173
- _authors = validator("authors", allow_reuse=True)(
174
- valstr("authors", accept_none=True)
183
+ _authors = field_validator("authors")(
184
+ classmethod(valstr("authors", accept_none=True))
175
185
  )
176
186
 
177
- @validator("tags")
187
+ @field_validator("tags")
188
+ @classmethod
178
189
  def validate_tags(cls, value):
179
190
  for i, tag in enumerate(value):
180
- value[i] = valstr(f"tags[{i}]")(tag)
181
- return val_unique_list("tags")(value)
191
+ value[i] = valstr(f"tags[{i}]")(cls, tag)
192
+ return val_unique_list("tags")(cls, value)
182
193
 
183
194
 
184
- class TaskImportV2(BaseModel, extra=Extra.forbid):
195
+ class TaskImportV2(BaseModel):
196
+ model_config = ConfigDict(extra="forbid")
185
197
 
186
198
  pkg_name: str
187
199
  version: Optional[str] = None
188
200
  name: str
189
- _pkg_name = validator("pkg_name", allow_reuse=True)(valstr("pkg_name"))
190
- _version = validator("version", allow_reuse=True)(
191
- valstr("version", accept_none=True)
201
+ _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
202
+ _version = field_validator("version")(
203
+ classmethod(valstr("version", accept_none=True))
192
204
  )
193
- _name = validator("name", allow_reuse=True)(valstr("name"))
205
+ _name = field_validator("name")(classmethod(valstr("name")))
194
206
 
195
207
 
196
208
  class TaskImportV2Legacy(BaseModel):
197
209
  source: str
198
- _source = validator("source", allow_reuse=True)(valstr("source"))
210
+ _source = field_validator("source")(classmethod(valstr("source")))
199
211
 
200
212
 
201
213
  class TaskExportV2(BaseModel):
202
-
203
214
  pkg_name: str
204
215
  version: Optional[str] = None
205
216
  name: str
206
217
 
207
- _pkg_name = validator("pkg_name", allow_reuse=True)(valstr("pkg_name"))
208
- _version = validator("version", allow_reuse=True)(
209
- valstr("version", accept_none=True)
218
+ _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
219
+ _version = field_validator("version")(
220
+ classmethod(valstr("version", accept_none=True))
210
221
  )
211
- _name = validator("name", allow_reuse=True)(valstr("name"))
222
+ _name = field_validator("name")(classmethod(valstr("name")))
@@ -3,9 +3,9 @@ from typing import Literal
3
3
  from typing import Optional
4
4
 
5
5
  from pydantic import BaseModel
6
- from pydantic import Extra
7
- from pydantic import root_validator
8
- from pydantic import validator
6
+ from pydantic import ConfigDict
7
+ from pydantic import field_validator
8
+ from pydantic import model_validator
9
9
 
10
10
  from .._validators import valstr
11
11
  from fractal_server.app.schemas.v2 import ManifestV2
@@ -21,7 +21,7 @@ class WheelFile(BaseModel):
21
21
  contents: bytes
22
22
 
23
23
 
24
- class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
24
+ class TaskCollectPipV2(BaseModel):
25
25
  """
26
26
  TaskCollectPipV2 class
27
27
 
@@ -45,35 +45,40 @@ class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
45
45
 
46
46
  """
47
47
 
48
+ model_config = ConfigDict(extra="forbid")
48
49
  package: Optional[str] = None
49
50
  package_version: Optional[str] = None
50
51
  package_extras: Optional[str] = None
51
52
  python_version: Optional[Literal["3.9", "3.10", "3.11", "3.12"]] = None
52
53
  pinned_package_versions: Optional[dict[str, str]] = None
53
54
 
54
- @validator("package")
55
+ @field_validator("package")
56
+ @classmethod
55
57
  def package_validator(cls, value: Optional[str]) -> Optional[str]:
56
58
  if value is None:
57
59
  return value
58
- value = valstr("package")(value)
60
+ value = valstr("package")(cls, value)
59
61
  validate_cmd(value, attribute_name="package")
60
62
  return value
61
63
 
62
- @validator("package_version")
64
+ @field_validator("package_version")
65
+ @classmethod
63
66
  def package_version_validator(cls, value: Optional[str]) -> Optional[str]:
64
67
  if value is None:
65
68
  return value
66
- value = valstr("package_version")(value)
69
+ value = valstr("package_version")(cls, value)
67
70
  validate_cmd(value, attribute_name="package_version")
68
71
  return value
69
72
 
70
- @validator("pinned_package_versions")
73
+ @field_validator("pinned_package_versions")
74
+ @classmethod
71
75
  def pinned_package_versions_validator(cls, value):
72
76
  if value is None:
73
77
  return value
74
78
  old_keys = list(value.keys())
75
79
  new_keys = [
76
- valstr(f"pinned_package_versions[{key}]")(key) for key in old_keys
80
+ valstr(f"pinned_package_versions[{key}]")(cls, key)
81
+ for key in old_keys
77
82
  ]
78
83
  if len(new_keys) != len(set(new_keys)):
79
84
  raise ValueError(
@@ -87,16 +92,17 @@ class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
87
92
  validate_cmd(version)
88
93
  return value
89
94
 
90
- @validator("package_extras")
95
+ @field_validator("package_extras")
96
+ @classmethod
91
97
  def package_extras_validator(cls, value: Optional[str]) -> Optional[str]:
92
98
  if value is None:
93
99
  return value
94
- value = valstr("package_extras")(value)
100
+ value = valstr("package_extras")(cls, value)
95
101
  validate_cmd(value, attribute_name="package_extras")
96
102
  return value
97
103
 
98
104
 
99
- class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
105
+ class TaskCollectCustomV2(BaseModel):
100
106
  """
101
107
  Attributes:
102
108
  manifest: Manifest of a Fractal task package (this is typically the
@@ -114,29 +120,31 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
114
120
  version: Optional version of tasks to be collected.
115
121
  """
116
122
 
123
+ model_config = ConfigDict(extra="forbid")
117
124
  manifest: ManifestV2
118
125
  python_interpreter: str
119
126
  label: str
120
- package_root: Optional[str]
121
- package_name: Optional[str]
122
- version: Optional[str]
127
+ package_root: Optional[str] = None
128
+ package_name: Optional[str] = None
129
+ version: Optional[str] = None
123
130
 
124
131
  # Valstr
125
- _python_interpreter = validator("python_interpreter", allow_reuse=True)(
126
- valstr("python_interpreter")
132
+ _python_interpreter = field_validator("python_interpreter")(
133
+ classmethod(valstr("python_interpreter"))
127
134
  )
128
- _label = validator("label", allow_reuse=True)(valstr("label"))
129
- _package_root = validator("package_root", allow_reuse=True)(
130
- valstr("package_root", accept_none=True)
135
+ _label = field_validator("label")(classmethod(valstr("label")))
136
+ _package_root = field_validator("package_root")(
137
+ classmethod(valstr("package_root", accept_none=True))
131
138
  )
132
- _package_name = validator("package_name", allow_reuse=True)(
133
- valstr("package_name", accept_none=True)
139
+ _package_name = field_validator("package_name")(
140
+ classmethod(valstr("package_name", accept_none=True))
134
141
  )
135
- _version = validator("version", allow_reuse=True)(
136
- valstr("version", accept_none=True)
142
+ _version = field_validator("version")(
143
+ classmethod(valstr("version", accept_none=True))
137
144
  )
138
145
 
139
- @root_validator(pre=True)
146
+ @model_validator(mode="before")
147
+ @classmethod
140
148
  def one_of_package_root_or_name(cls, values):
141
149
  package_root = values.get("package_root")
142
150
  package_name = values.get("package_name")
@@ -149,18 +157,20 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
149
157
  )
150
158
  return values
151
159
 
152
- @validator("package_name")
160
+ @field_validator("package_name")
161
+ @classmethod
153
162
  def package_name_validator(cls, value: str):
154
163
  """
155
164
  Remove all whitespace characters, then check for invalid code.
156
165
  """
157
166
  if value is not None:
158
167
  validate_cmd(value)
159
- value = valstr("package_name")(value)
168
+ value = valstr("package_name")(cls, value)
160
169
  value = value.replace(" ", "")
161
170
  return value
162
171
 
163
- @validator("package_root")
172
+ @field_validator("package_root")
173
+ @classmethod
164
174
  def package_root_validator(cls, value):
165
175
  if (value is not None) and (not Path(value).is_absolute()):
166
176
  raise ValueError(
@@ -168,7 +178,8 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
168
178
  )
169
179
  return value
170
180
 
171
- @validator("python_interpreter")
181
+ @field_validator("python_interpreter")
182
+ @classmethod
172
183
  def python_interpreter_validator(cls, value):
173
184
  if not Path(value).is_absolute():
174
185
  raise ValueError(