fractal-server 2.14.0a3__py3-none-any.whl → 2.14.0a4__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 (47) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +3 -1
  3. fractal_server/app/history/__init__.py +4 -4
  4. fractal_server/app/history/image_updates.py +124 -143
  5. fractal_server/app/history/status_enum.py +2 -2
  6. fractal_server/app/models/v2/__init__.py +6 -4
  7. fractal_server/app/models/v2/history.py +44 -20
  8. fractal_server/app/routes/api/__init__.py +1 -1
  9. fractal_server/app/routes/api/v2/__init__.py +4 -0
  10. fractal_server/app/routes/api/v2/_aux_functions_history.py +49 -0
  11. fractal_server/app/routes/api/v2/dataset.py +0 -12
  12. fractal_server/app/routes/api/v2/history.py +301 -186
  13. fractal_server/app/routes/api/v2/project.py +0 -25
  14. fractal_server/app/routes/api/v2/status_legacy.py +168 -0
  15. fractal_server/app/routes/api/v2/workflow.py +2 -17
  16. fractal_server/app/routes/api/v2/workflowtask.py +41 -71
  17. fractal_server/app/routes/auth/oauth.py +5 -3
  18. fractal_server/app/runner/executors/local/runner.py +10 -55
  19. fractal_server/app/runner/executors/slurm_sudo/runner.py +171 -108
  20. fractal_server/app/runner/v2/__init__.py +0 -20
  21. fractal_server/app/runner/v2/runner.py +45 -58
  22. fractal_server/app/runner/v2/runner_functions.py +164 -22
  23. fractal_server/app/schemas/_validators.py +13 -24
  24. fractal_server/app/schemas/user.py +10 -7
  25. fractal_server/app/schemas/user_settings.py +9 -21
  26. fractal_server/app/schemas/v2/dataset.py +8 -6
  27. fractal_server/app/schemas/v2/job.py +9 -5
  28. fractal_server/app/schemas/v2/manifest.py +2 -6
  29. fractal_server/app/schemas/v2/project.py +9 -7
  30. fractal_server/app/schemas/v2/task.py +41 -77
  31. fractal_server/app/schemas/v2/task_collection.py +14 -32
  32. fractal_server/app/schemas/v2/task_group.py +10 -9
  33. fractal_server/app/schemas/v2/workflow.py +10 -11
  34. fractal_server/app/security/signup_email.py +2 -2
  35. fractal_server/config.py +31 -32
  36. fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
  37. fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
  38. fractal_server/tasks/v2/utils_templates.py +6 -0
  39. {fractal_server-2.14.0a3.dist-info → fractal_server-2.14.0a4.dist-info}/METADATA +1 -1
  40. {fractal_server-2.14.0a3.dist-info → fractal_server-2.14.0a4.dist-info}/RECORD +43 -44
  41. fractal_server/app/runner/executors/slurm_sudo/_executor_wait_thread.py +0 -130
  42. fractal_server/app/schemas/v2/history.py +0 -23
  43. fractal_server/migrations/versions/87cd72a537a2_add_historyitem_table.py +0 -68
  44. fractal_server/migrations/versions/954ddc64425a_image_status.py +0 -63
  45. {fractal_server-2.14.0a3.dist-info → fractal_server-2.14.0a4.dist-info}/LICENSE +0 -0
  46. {fractal_server-2.14.0a3.dist-info → fractal_server-2.14.0a4.dist-info}/WHEEL +0 -0
  47. {fractal_server-2.14.0a3.dist-info → fractal_server-2.14.0a4.dist-info}/entry_points.txt +0 -0
@@ -12,8 +12,9 @@ from pydantic.types import AwareDatetime
12
12
 
13
13
  from .._filter_validators import validate_attribute_filters
14
14
  from .._filter_validators import validate_type_filters
15
+ from .._validators import cant_set_none
16
+ from .._validators import NonEmptyString
15
17
  from .._validators import root_validate_dict_keys
16
- from .._validators import valstr
17
18
  from .project import ProjectReadV2
18
19
  from fractal_server.images import SingleImage
19
20
  from fractal_server.images.models import AttributeFiltersType
@@ -23,7 +24,7 @@ from fractal_server.urls import normalize_url
23
24
  class DatasetCreateV2(BaseModel):
24
25
  model_config = ConfigDict(extra="forbid")
25
26
 
26
- name: str
27
+ name: NonEmptyString
27
28
 
28
29
  zarr_dir: Optional[str] = None
29
30
 
@@ -42,8 +43,6 @@ class DatasetCreateV2(BaseModel):
42
43
  classmethod(validate_attribute_filters)
43
44
  )
44
45
 
45
- _name = field_validator("name")(classmethod(valstr("name")))
46
-
47
46
  @field_validator("zarr_dir")
48
47
  @classmethod
49
48
  def normalize_zarr_dir(cls, v: Optional[str]) -> Optional[str]:
@@ -73,7 +72,7 @@ class DatasetReadV2(BaseModel):
73
72
  class DatasetUpdateV2(BaseModel):
74
73
  model_config = ConfigDict(extra="forbid")
75
74
 
76
- name: Optional[str] = None
75
+ name: Optional[NonEmptyString] = None
77
76
  zarr_dir: Optional[str] = None
78
77
  type_filters: Optional[dict[str, bool]] = None
79
78
  attribute_filters: Optional[dict[str, list[Any]]] = None
@@ -90,7 +89,10 @@ class DatasetUpdateV2(BaseModel):
90
89
  classmethod(validate_attribute_filters)
91
90
  )
92
91
 
93
- _name = field_validator("name")(classmethod(valstr("name")))
92
+ @field_validator("name")
93
+ @classmethod
94
+ def _cant_set_none(cls, v):
95
+ return cant_set_none(v)
94
96
 
95
97
  @field_validator("zarr_dir")
96
98
  @classmethod
@@ -13,8 +13,9 @@ from pydantic.types import AwareDatetime
13
13
  from pydantic.types import StrictStr
14
14
 
15
15
  from .._filter_validators import validate_attribute_filters
16
+ from .._validators import cant_set_none
17
+ from .._validators import NonEmptyString
16
18
  from .._validators import root_validate_dict_keys
17
- from .._validators import valstr
18
19
  from .dumps import DatasetDumpV2
19
20
  from .dumps import ProjectDumpV2
20
21
  from .dumps import WorkflowDumpV2
@@ -49,14 +50,17 @@ class JobCreateV2(BaseModel):
49
50
  first_task_index: Optional[int] = None
50
51
  last_task_index: Optional[int] = None
51
52
  slurm_account: Optional[StrictStr] = None
52
- worker_init: Optional[str] = None
53
+ worker_init: Optional[NonEmptyString] = None
53
54
 
54
55
  attribute_filters: AttributeFiltersType = Field(default_factory=dict)
55
56
 
56
57
  # Validators
57
- _worker_init = field_validator("worker_init")(
58
- classmethod(valstr("worker_init"))
59
- )
58
+
59
+ @field_validator("worker_init")
60
+ @classmethod
61
+ def _cant_set_none(cls, v):
62
+ return cant_set_none(v)
63
+
60
64
  _dict_keys = model_validator(mode="before")(
61
65
  classmethod(root_validate_dict_keys)
62
66
  )
@@ -7,7 +7,7 @@ from pydantic import field_validator
7
7
  from pydantic import HttpUrl
8
8
  from pydantic import model_validator
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import NonEmptyString
11
11
 
12
12
 
13
13
  class TaskManifestV2(BaseModel):
@@ -138,7 +138,7 @@ class ManifestV2(BaseModel):
138
138
  task_list: list[TaskManifestV2]
139
139
  has_args_schemas: bool = False
140
140
  args_schema_version: Optional[str] = None
141
- authors: Optional[str] = None
141
+ authors: Optional[NonEmptyString] = None
142
142
 
143
143
  @model_validator(mode="after")
144
144
  def _check_args_schemas_are_present(self):
@@ -181,7 +181,3 @@ class ManifestV2(BaseModel):
181
181
  if value != "2":
182
182
  raise ValueError(f"Wrong manifest version (given {value})")
183
183
  return value
184
-
185
- _authors = field_validator("authors")(
186
- classmethod(valstr("authors", accept_none=True))
187
- )
@@ -7,16 +7,15 @@ from pydantic import field_serializer
7
7
  from pydantic import field_validator
8
8
  from pydantic.types import AwareDatetime
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import cant_set_none
11
+ from .._validators import NonEmptyString
11
12
 
12
13
 
13
14
  class ProjectCreateV2(BaseModel):
14
15
 
15
16
  model_config = ConfigDict(extra="forbid")
16
17
 
17
- name: str
18
- # Validators
19
- _name = field_validator("name")(classmethod(valstr("name")))
18
+ name: NonEmptyString
20
19
 
21
20
 
22
21
  class ProjectReadV2(BaseModel):
@@ -34,6 +33,9 @@ class ProjectUpdateV2(BaseModel):
34
33
 
35
34
  model_config = ConfigDict(extra="forbid")
36
35
 
37
- name: Optional[str] = None
38
- # Validators
39
- _name = field_validator("name")(classmethod(valstr("name")))
36
+ name: Optional[NonEmptyString] = None
37
+
38
+ @field_validator("name")
39
+ @classmethod
40
+ def _cant_set_none(cls, v):
41
+ return cant_set_none(v)
@@ -9,38 +9,50 @@ from pydantic import field_validator
9
9
  from pydantic import HttpUrl
10
10
  from pydantic import model_validator
11
11
 
12
+ from .._validators import cant_set_none
13
+ from fractal_server.app.schemas._validators import NonEmptyString
12
14
  from fractal_server.app.schemas._validators import val_unique_list
13
15
  from fractal_server.app.schemas._validators import valdict_keys
14
- from fractal_server.app.schemas._validators import valstr
15
16
  from fractal_server.string_tools import validate_cmd
16
17
 
17
18
 
18
19
  class TaskCreateV2(BaseModel):
19
20
  model_config = ConfigDict(extra="forbid")
20
21
 
21
- name: str
22
+ name: NonEmptyString
22
23
 
23
- command_non_parallel: Optional[str] = None
24
- command_parallel: Optional[str] = None
24
+ command_non_parallel: Optional[NonEmptyString] = None
25
+ command_parallel: Optional[NonEmptyString] = None
25
26
 
26
27
  meta_non_parallel: Optional[dict[str, Any]] = None
27
28
  meta_parallel: Optional[dict[str, Any]] = None
28
- version: Optional[str] = None
29
+ version: Optional[NonEmptyString] = None
29
30
  args_schema_non_parallel: Optional[dict[str, Any]] = None
30
31
  args_schema_parallel: Optional[dict[str, Any]] = None
31
- args_schema_version: Optional[str] = None
32
+ args_schema_version: Optional[NonEmptyString] = None
32
33
  docs_info: Optional[str] = None
33
34
  docs_link: Optional[str] = None
34
35
 
35
36
  input_types: dict[str, bool] = Field(default={})
36
37
  output_types: dict[str, bool] = Field(default={})
37
38
 
38
- category: Optional[str] = None
39
- modality: Optional[str] = None
40
- tags: list[str] = Field(default_factory=list)
41
- authors: Optional[str] = None
39
+ category: Optional[NonEmptyString] = None
40
+ modality: Optional[NonEmptyString] = None
41
+ tags: list[NonEmptyString] = Field(default_factory=list)
42
+ authors: Optional[NonEmptyString] = None
42
43
 
43
44
  # Validators
45
+
46
+ @field_validator(
47
+ "command_non_parallel",
48
+ "command_parallel",
49
+ "version",
50
+ "args_schema_version",
51
+ )
52
+ @classmethod
53
+ def _cant_set_none(cls, v):
54
+ return cant_set_none(v)
55
+
44
56
  @model_validator(mode="after")
45
57
  def validate_commands(self):
46
58
  command_parallel = self.command_parallel
@@ -57,15 +69,6 @@ class TaskCreateV2(BaseModel):
57
69
 
58
70
  return self
59
71
 
60
- _name = field_validator("name")(classmethod(valstr("name")))
61
- _command_non_parallel = field_validator("command_non_parallel")(
62
- classmethod(valstr("command_non_parallel"))
63
- )
64
- _command_parallel = field_validator("command_parallel")(
65
- classmethod(valstr("command_parallel"))
66
- )
67
- _version = field_validator("version")(classmethod(valstr("version")))
68
-
69
72
  _meta_non_parallel = field_validator("meta_non_parallel")(
70
73
  classmethod(valdict_keys("meta_non_parallel"))
71
74
  )
@@ -78,9 +81,6 @@ class TaskCreateV2(BaseModel):
78
81
  _args_schema_parallel = field_validator("args_schema_parallel")(
79
82
  classmethod(valdict_keys("args_schema_parallel"))
80
83
  )
81
- _args_schema_version = field_validator("args_schema_version")(
82
- classmethod(valstr("args_schema_version"))
83
- )
84
84
  _input_types = field_validator("input_types")(
85
85
  classmethod(valdict_keys("input_types"))
86
86
  )
@@ -88,21 +88,9 @@ class TaskCreateV2(BaseModel):
88
88
  classmethod(valdict_keys("output_types"))
89
89
  )
90
90
 
91
- _category = field_validator("category")(
92
- classmethod(valstr("category", accept_none=True))
93
- )
94
- _modality = field_validator("modality")(
95
- classmethod(valstr("modality", accept_none=True))
96
- )
97
- _authors = field_validator("authors")(
98
- classmethod(valstr("authors", accept_none=True))
99
- )
100
-
101
91
  @field_validator("tags")
102
92
  @classmethod
103
93
  def validate_list_of_strings(cls, value):
104
- for i, tag in enumerate(value):
105
- value[i] = valstr(f"tags[{i}]")(cls, tag)
106
94
  return val_unique_list("tags")(cls, value)
107
95
 
108
96
  @field_validator("docs_link", mode="after")
@@ -143,17 +131,23 @@ class TaskReadV2(BaseModel):
143
131
  class TaskUpdateV2(BaseModel):
144
132
  model_config = ConfigDict(extra="forbid")
145
133
 
146
- command_parallel: Optional[str] = None
147
- command_non_parallel: Optional[str] = None
134
+ command_parallel: Optional[NonEmptyString] = None
135
+ command_non_parallel: Optional[NonEmptyString] = None
148
136
  input_types: Optional[dict[str, bool]] = None
149
137
  output_types: Optional[dict[str, bool]] = None
150
138
 
151
- category: Optional[str] = None
152
- modality: Optional[str] = None
153
- authors: Optional[str] = None
154
- tags: Optional[list[str]] = None
139
+ category: Optional[NonEmptyString] = None
140
+ modality: Optional[NonEmptyString] = None
141
+ authors: Optional[NonEmptyString] = None
142
+ tags: Optional[list[NonEmptyString]] = None
155
143
 
156
144
  # Validators
145
+
146
+ @field_validator("command_parallel", "command_non_parallel")
147
+ @classmethod
148
+ def _cant_set_none(cls, v):
149
+ return cant_set_none(v)
150
+
157
151
  @field_validator("input_types", "output_types")
158
152
  @classmethod
159
153
  def val_is_dict(cls, v):
@@ -161,12 +155,6 @@ class TaskUpdateV2(BaseModel):
161
155
  raise ValueError
162
156
  return v
163
157
 
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"))
169
- )
170
158
  _input_types = field_validator("input_types")(
171
159
  classmethod(valdict_keys("input_types"))
172
160
  )
@@ -174,49 +162,25 @@ class TaskUpdateV2(BaseModel):
174
162
  classmethod(valdict_keys("output_types"))
175
163
  )
176
164
 
177
- _category = field_validator("category")(
178
- classmethod(valstr("category", accept_none=True))
179
- )
180
- _modality = field_validator("modality")(
181
- classmethod(valstr("modality", accept_none=True))
182
- )
183
- _authors = field_validator("authors")(
184
- classmethod(valstr("authors", accept_none=True))
185
- )
186
-
187
165
  @field_validator("tags")
188
166
  @classmethod
189
167
  def validate_tags(cls, value):
190
- for i, tag in enumerate(value):
191
- value[i] = valstr(f"tags[{i}]")(cls, tag)
192
168
  return val_unique_list("tags")(cls, value)
193
169
 
194
170
 
195
171
  class TaskImportV2(BaseModel):
196
172
  model_config = ConfigDict(extra="forbid")
197
173
 
198
- pkg_name: str
199
- version: Optional[str] = None
200
- name: str
201
- _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
202
- _version = field_validator("version")(
203
- classmethod(valstr("version", accept_none=True))
204
- )
205
- _name = field_validator("name")(classmethod(valstr("name")))
174
+ pkg_name: NonEmptyString
175
+ version: Optional[NonEmptyString] = None
176
+ name: NonEmptyString
206
177
 
207
178
 
208
179
  class TaskImportV2Legacy(BaseModel):
209
- source: str
210
- _source = field_validator("source")(classmethod(valstr("source")))
180
+ source: NonEmptyString
211
181
 
212
182
 
213
183
  class TaskExportV2(BaseModel):
214
- pkg_name: str
215
- version: Optional[str] = None
216
- name: str
217
-
218
- _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
219
- _version = field_validator("version")(
220
- classmethod(valstr("version", accept_none=True))
221
- )
222
- _name = field_validator("name")(classmethod(valstr("name")))
184
+ pkg_name: NonEmptyString
185
+ version: Optional[NonEmptyString] = None
186
+ name: NonEmptyString
@@ -7,7 +7,7 @@ from pydantic import ConfigDict
7
7
  from pydantic import field_validator
8
8
  from pydantic import model_validator
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import NonEmptyString
11
11
  from fractal_server.app.schemas.v2 import ManifestV2
12
12
  from fractal_server.string_tools import validate_cmd
13
13
 
@@ -46,9 +46,9 @@ class TaskCollectPipV2(BaseModel):
46
46
  """
47
47
 
48
48
  model_config = ConfigDict(extra="forbid")
49
- package: Optional[str] = None
50
- package_version: Optional[str] = None
51
- package_extras: Optional[str] = None
49
+ package: Optional[NonEmptyString] = None
50
+ package_version: Optional[NonEmptyString] = None
51
+ package_extras: Optional[NonEmptyString] = None
52
52
  python_version: Optional[Literal["3.9", "3.10", "3.11", "3.12"]] = None
53
53
  pinned_package_versions: Optional[dict[str, str]] = None
54
54
 
@@ -57,7 +57,6 @@ class TaskCollectPipV2(BaseModel):
57
57
  def package_validator(cls, value: Optional[str]) -> Optional[str]:
58
58
  if value is None:
59
59
  return value
60
- value = valstr("package")(cls, value)
61
60
  validate_cmd(value, attribute_name="package")
62
61
  return value
63
62
 
@@ -66,7 +65,6 @@ class TaskCollectPipV2(BaseModel):
66
65
  def package_version_validator(cls, value: Optional[str]) -> Optional[str]:
67
66
  if value is None:
68
67
  return value
69
- value = valstr("package_version")(cls, value)
70
68
  validate_cmd(value, attribute_name="package_version")
71
69
  return value
72
70
 
@@ -75,11 +73,11 @@ class TaskCollectPipV2(BaseModel):
75
73
  def pinned_package_versions_validator(cls, value):
76
74
  if value is None:
77
75
  return value
76
+
78
77
  old_keys = list(value.keys())
79
- new_keys = [
80
- valstr(f"pinned_package_versions[{key}]")(cls, key)
81
- for key in old_keys
82
- ]
78
+ new_keys = [key.strip() for key in old_keys]
79
+ if any(k == "" for k in new_keys):
80
+ raise ValueError(f"Empty string in {new_keys}.")
83
81
  if len(new_keys) != len(set(new_keys)):
84
82
  raise ValueError(
85
83
  f"Dictionary contains multiple identical keys: {value}."
@@ -87,6 +85,7 @@ class TaskCollectPipV2(BaseModel):
87
85
  for old_key, new_key in zip(old_keys, new_keys):
88
86
  if new_key != old_key:
89
87
  value[new_key] = value.pop(old_key)
88
+
90
89
  for pkg, version in value.items():
91
90
  validate_cmd(pkg)
92
91
  validate_cmd(version)
@@ -97,7 +96,6 @@ class TaskCollectPipV2(BaseModel):
97
96
  def package_extras_validator(cls, value: Optional[str]) -> Optional[str]:
98
97
  if value is None:
99
98
  return value
100
- value = valstr("package_extras")(cls, value)
101
99
  validate_cmd(value, attribute_name="package_extras")
102
100
  return value
103
101
 
@@ -122,26 +120,11 @@ class TaskCollectCustomV2(BaseModel):
122
120
 
123
121
  model_config = ConfigDict(extra="forbid")
124
122
  manifest: ManifestV2
125
- python_interpreter: str
126
- label: str
127
- package_root: Optional[str] = None
128
- package_name: Optional[str] = None
129
- version: Optional[str] = None
130
-
131
- # Valstr
132
- _python_interpreter = field_validator("python_interpreter")(
133
- classmethod(valstr("python_interpreter"))
134
- )
135
- _label = field_validator("label")(classmethod(valstr("label")))
136
- _package_root = field_validator("package_root")(
137
- classmethod(valstr("package_root", accept_none=True))
138
- )
139
- _package_name = field_validator("package_name")(
140
- classmethod(valstr("package_name", accept_none=True))
141
- )
142
- _version = field_validator("version")(
143
- classmethod(valstr("version", accept_none=True))
144
- )
123
+ python_interpreter: NonEmptyString
124
+ label: NonEmptyString
125
+ package_root: Optional[NonEmptyString] = None
126
+ package_name: Optional[NonEmptyString] = None
127
+ version: Optional[NonEmptyString] = None
145
128
 
146
129
  @model_validator(mode="before")
147
130
  @classmethod
@@ -165,7 +148,6 @@ class TaskCollectCustomV2(BaseModel):
165
148
  """
166
149
  if value is not None:
167
150
  validate_cmd(value)
168
- value = valstr("package_name")(cls, value)
169
151
  value = value.replace(" ", "")
170
152
  return value
171
153
 
@@ -9,9 +9,10 @@ from pydantic import field_serializer
9
9
  from pydantic import field_validator
10
10
  from pydantic.types import AwareDatetime
11
11
 
12
+ from .._validators import cant_set_none
13
+ from .._validators import NonEmptyString
12
14
  from .._validators import val_absolute_path
13
15
  from .._validators import valdict_keys
14
- from .._validators import valstr
15
16
  from .task import TaskReadV2
16
17
 
17
18
 
@@ -42,15 +43,21 @@ class TaskGroupCreateV2(BaseModel):
42
43
  origin: TaskGroupV2OriginEnum
43
44
  pkg_name: str
44
45
  version: Optional[str] = None
45
- python_version: Optional[str] = None
46
+ python_version: Optional[NonEmptyString] = None
46
47
  path: Optional[str] = None
47
48
  venv_path: Optional[str] = None
48
49
  wheel_path: Optional[str] = None
49
- pip_extras: Optional[str] = None
50
+ pip_extras: Optional[NonEmptyString] = None
50
51
  pip_freeze: Optional[str] = None
51
52
  pinned_package_versions: dict[str, str] = Field(default_factory=dict)
52
53
 
53
54
  # Validators
55
+
56
+ @field_validator("python_version", "pip_extras")
57
+ @classmethod
58
+ def _cant_set_none(cls, v):
59
+ return cant_set_none(v)
60
+
54
61
  _path = field_validator("path")(classmethod(val_absolute_path("path")))
55
62
  _venv_path = field_validator("venv_path")(
56
63
  classmethod(val_absolute_path("venv_path"))
@@ -61,12 +68,6 @@ class TaskGroupCreateV2(BaseModel):
61
68
  _pinned_package_versions = field_validator("pinned_package_versions")(
62
69
  valdict_keys("pinned_package_versions")
63
70
  )
64
- _pip_extras = field_validator("pip_extras")(
65
- classmethod(valstr("pip_extras"))
66
- )
67
- _python_version = field_validator("python_version")(
68
- classmethod(valstr("python_version"))
69
- )
70
71
 
71
72
 
72
73
  class TaskGroupCreateV2Strict(TaskGroupCreateV2):
@@ -7,7 +7,8 @@ from pydantic import field_serializer
7
7
  from pydantic import field_validator
8
8
  from pydantic.types import AwareDatetime
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import cant_set_none
11
+ from .._validators import NonEmptyString
11
12
  from .project import ProjectReadV2
12
13
  from .workflowtask import WorkflowTaskExportV2
13
14
  from .workflowtask import WorkflowTaskImportV2
@@ -19,10 +20,7 @@ class WorkflowCreateV2(BaseModel):
19
20
 
20
21
  model_config = ConfigDict(extra="forbid")
21
22
 
22
- name: str
23
-
24
- # Validators
25
- _name = field_validator("name")(classmethod(valstr("name")))
23
+ name: NonEmptyString
26
24
 
27
25
 
28
26
  class WorkflowReadV2(BaseModel):
@@ -47,11 +45,15 @@ class WorkflowUpdateV2(BaseModel):
47
45
 
48
46
  model_config = ConfigDict(extra="forbid")
49
47
 
50
- name: Optional[str] = None
48
+ name: Optional[NonEmptyString] = None
51
49
  reordered_workflowtask_ids: Optional[list[int]] = None
52
50
 
53
51
  # Validators
54
- _name = field_validator("name")(classmethod(valstr("name")))
52
+
53
+ @field_validator("name")
54
+ @classmethod
55
+ def _cant_set_none(cls, v):
56
+ return cant_set_none(v)
55
57
 
56
58
  @field_validator("reordered_workflowtask_ids")
57
59
  @classmethod
@@ -72,12 +74,9 @@ class WorkflowImportV2(BaseModel):
72
74
  """
73
75
 
74
76
  model_config = ConfigDict(extra="forbid")
75
- name: str
77
+ name: NonEmptyString
76
78
  task_list: list[WorkflowTaskImportV2]
77
79
 
78
- # Validators
79
- _name = field_validator("name")(classmethod(valstr("name")))
80
-
81
80
 
82
81
  class WorkflowExportV2(BaseModel):
83
82
  """
@@ -36,8 +36,8 @@ def mail_new_oauth_signup(msg: str, email_settings: MailSettings):
36
36
  server.ehlo()
37
37
  if email_settings.use_login:
38
38
  password = (
39
- Fernet(email_settings.encryption_key)
40
- .decrypt(email_settings.encrypted_password)
39
+ Fernet(email_settings.encryption_key.get_secret_value())
40
+ .decrypt(email_settings.encrypted_password.get_secret_value())
41
41
  .decode("utf-8")
42
42
  )
43
43
  server.login(user=email_settings.sender, password=password)